hfs 0.57.26 → 0.57.27-beta2
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/admin/assets/{index-CHMhmNYS.js → index-DSkhXlWX.js} +103 -103
- package/admin/assets/{sha512-Dsv49IAs.js → sha512-CoddZKeD.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{index-legacy-BI8icwFp.js → index-legacy-C4Gclsbz.js} +4 -4
- package/frontend/assets/{sha512-legacy-D82alHBq.js → sha512-legacy-C-FMk44u.js} +1 -1
- package/frontend/index.html +1 -1
- package/package.json +4 -3
- package/src/QuickZipStream.js +11 -8
- package/src/SendList.js +10 -8
- package/src/ThrottledStream.js +8 -6
- package/src/acme.js +5 -7
- package/src/adminApis.js +6 -7
- package/src/api.accounts.js +4 -6
- package/src/api.auth.js +7 -11
- package/src/api.get_file_list.js +11 -16
- package/src/api.lang.js +1 -1
- package/src/api.monitor.js +2 -3
- package/src/api.net.js +1 -2
- package/src/api.plugins.js +8 -10
- package/src/api.vfs.js +14 -19
- package/src/apiMiddleware.js +4 -4
- package/src/auth.js +6 -9
- package/src/basicWeb.js +1 -2
- package/src/block.js +1 -1
- package/src/commands.js +9 -9
- package/src/comments.js +4 -5
- package/src/config.js +8 -9
- package/src/connections.js +11 -6
- package/src/consoleLog.js +6 -6
- package/src/const.js +4 -4
- package/src/cross.js +19 -21
- package/src/customHtml.js +3 -4
- package/src/ddns.js +4 -4
- package/src/debounceAsync.js +3 -3
- package/src/errorPages.js +2 -3
- package/src/events.js +7 -10
- package/src/fileAttr.js +6 -6
- package/src/first.js +13 -8
- package/src/frontEndApis.js +5 -5
- package/src/geo.js +3 -5
- package/src/github.js +14 -17
- package/src/i18n.js +3 -4
- package/src/icons.js +1 -1
- package/src/index.js +2 -3
- package/src/lang.js +5 -6
- package/src/listen.js +25 -28
- package/src/log.js +18 -17
- package/src/middlewares.js +12 -15
- package/src/misc.js +9 -11
- package/src/nat.js +14 -16
- package/src/outboundProxy.js +7 -7
- package/src/perm.js +7 -12
- package/src/persistence.js +1 -1
- package/src/plugins.js +62 -73
- package/src/roots.js +1 -2
- package/src/selfCheck.js +5 -4
- package/src/serveFile.js +6 -7
- package/src/serveGuiAndSharedFiles.js +7 -9
- package/src/serveGuiFiles.js +11 -20
- package/src/stat.js +40 -0
- package/src/statWorker.js +11 -0
- package/src/throttler.js +5 -7
- package/src/update.js +7 -7
- package/src/upload.js +11 -13
- package/src/util-files.js +42 -32
- package/src/util-http.js +80 -29
- package/src/util-os.js +2 -4
- package/src/vfs.js +77 -68
- package/src/walkDir.js +9 -9
- package/src/watchLoad.js +4 -4
- package/src/zip.js +5 -6
package/src/vfs.js
CHANGED
|
@@ -56,7 +56,6 @@ function permsFromParent(parent, child) {
|
|
|
56
56
|
return lodash_1.default.isEmpty(ret) ? undefined : ret;
|
|
57
57
|
}
|
|
58
58
|
function inheritFromParent(child) {
|
|
59
|
-
var _a, _b, _c;
|
|
60
59
|
const { parent } = child;
|
|
61
60
|
if (!parent)
|
|
62
61
|
return;
|
|
@@ -64,11 +63,11 @@ function inheritFromParent(child) {
|
|
|
64
63
|
if (typeof parent.mime === 'object' && typeof child.mime === 'object')
|
|
65
64
|
lodash_1.default.defaults(child.mime, parent.mime);
|
|
66
65
|
else if (parent.mime)
|
|
67
|
-
|
|
66
|
+
child.mime ??= parent.mime;
|
|
68
67
|
if (parent.accept)
|
|
69
|
-
|
|
68
|
+
child.accept ??= parent.accept;
|
|
70
69
|
if (parent.default)
|
|
71
|
-
|
|
70
|
+
child.default ??= parent.default;
|
|
72
71
|
return child;
|
|
73
72
|
}
|
|
74
73
|
function isSameFilenameAs(name) {
|
|
@@ -79,15 +78,14 @@ function normalizeFilename(x) {
|
|
|
79
78
|
return (const_1.IS_WINDOWS || const_1.IS_MAC ? x.toLocaleLowerCase() : x).normalize();
|
|
80
79
|
}
|
|
81
80
|
async function applyParentToChild(child, parent, name) {
|
|
82
|
-
var _a, _b;
|
|
83
81
|
const ret = {
|
|
84
82
|
original: child, // this can be overridden by passing an 'original' in `child`
|
|
85
83
|
...child,
|
|
86
|
-
isFolder:
|
|
84
|
+
isFolder: child?.isFolder ?? (child?.children?.length > 0 || undefined), // isFolder is hidden in original node, so we must copy it explicitly
|
|
87
85
|
isTemp: true,
|
|
88
86
|
parent,
|
|
89
87
|
};
|
|
90
|
-
name
|
|
88
|
+
name ||= child ? getNodeName(child) : '';
|
|
91
89
|
inheritMasks(ret, parent, name);
|
|
92
90
|
await parentMaskApplier(parent)(ret, name);
|
|
93
91
|
inheritFromParent(ret);
|
|
@@ -105,15 +103,12 @@ async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
|
105
103
|
const ret = await getNodeByName(name, parent);
|
|
106
104
|
if (!ret)
|
|
107
105
|
return;
|
|
108
|
-
if (rest ||
|
|
106
|
+
if (rest || ret?.original)
|
|
109
107
|
return urlToNode(rest, ctx, ret, getRest);
|
|
110
108
|
if (ret.source)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
ret.isFolder = (await nodeStats(ret)).isDirectory(); // throws if it doesn't exist on disk
|
|
115
|
-
}
|
|
116
|
-
catch (_a) {
|
|
109
|
+
if (!showHiddenFiles.get() && await isHiddenFile(ret.source))
|
|
110
|
+
throw 'hiddenFile';
|
|
111
|
+
else if (await setIsFolder(ret) === undefined) { // undefined = not found on disk
|
|
117
112
|
if (!getRest)
|
|
118
113
|
return;
|
|
119
114
|
const rest = ret.source.slice(parent.source.length); // parent has source, otherwise !ret.source || ret.original
|
|
@@ -122,23 +117,24 @@ async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
|
122
117
|
}
|
|
123
118
|
return ret;
|
|
124
119
|
}
|
|
125
|
-
async function nodeStats(
|
|
126
|
-
if (
|
|
127
|
-
return
|
|
128
|
-
const stats =
|
|
129
|
-
|
|
120
|
+
async function nodeStats(node) {
|
|
121
|
+
if (node.stats || !node.source)
|
|
122
|
+
return node.stats;
|
|
123
|
+
const stats = (0, misc_1.statWithTimeout)(node.source).catch(() => {
|
|
124
|
+
(0, misc_1.setHidden)(node, { stats: null }); // don't cache rejected promises
|
|
125
|
+
});
|
|
126
|
+
(0, misc_1.setHidden)(node, { stats });
|
|
130
127
|
return stats;
|
|
131
128
|
}
|
|
132
129
|
async function isHiddenFile(path) {
|
|
133
|
-
return const_1.IS_WINDOWS ? new Promise(res => fswin_1.default.getAttributes(path, x => res(x
|
|
130
|
+
return const_1.IS_WINDOWS ? new Promise(res => fswin_1.default.getAttributes(path, x => res(x?.IS_HIDDEN)))
|
|
134
131
|
: path[path.lastIndexOf('/') + 1] === '.';
|
|
135
132
|
}
|
|
136
133
|
async function getNodeByName(name, parent) {
|
|
137
|
-
var _a;
|
|
138
134
|
// does the tree node have a child that goes by this name, otherwise attempt disk
|
|
139
|
-
const child =
|
|
135
|
+
const child = parent.children?.find(isSameFilenameAs(name)) || await childFromDisk();
|
|
140
136
|
return child && applyParentToChild(child, parent, name);
|
|
141
|
-
function childFromDisk() {
|
|
137
|
+
async function childFromDisk() {
|
|
142
138
|
if (!parent.source)
|
|
143
139
|
return;
|
|
144
140
|
const ret = {};
|
|
@@ -155,18 +151,26 @@ async function getNodeByName(name, parent) {
|
|
|
155
151
|
return;
|
|
156
152
|
ret.source = (0, path_1.join)(parent.source, onDisk);
|
|
157
153
|
ret.original = undefined; // this will overwrite the 'original' set in applyParentToChild, so we know this is not part of the vfs
|
|
154
|
+
await setIsFolder(ret);
|
|
158
155
|
return ret;
|
|
159
156
|
}
|
|
160
157
|
}
|
|
158
|
+
async function setIsFolder(node) {
|
|
159
|
+
if (!node.source)
|
|
160
|
+
return;
|
|
161
|
+
const isFolder = /[\\/]$/.test(node.source) || await nodeStats(node).then(x => x?.isDirectory(), () => undefined);
|
|
162
|
+
(0, misc_1.setHidden)(node, { isFolder });
|
|
163
|
+
return isFolder;
|
|
164
|
+
}
|
|
161
165
|
exports.vfs = {};
|
|
162
|
-
(0, config_1.defineConfig)('vfs', exports.vfs).sub(
|
|
166
|
+
(0, config_1.defineConfig)('vfs', exports.vfs).sub(async (x) => {
|
|
167
|
+
await reviewVfs(x);
|
|
168
|
+
console.log('VFS ready');
|
|
169
|
+
});
|
|
163
170
|
async function reviewVfs(data = exports.vfs) {
|
|
164
171
|
await (async function recur(node) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const isFolder = /[\\/]$/.test(node.source) || ((_b = (await nodeStats(node).catch(() => { }))) === null || _b === void 0 ? void 0 : _b.isDirectory());
|
|
168
|
-
(0, misc_1.setHidden)(node, { isFolder });
|
|
169
|
-
}
|
|
172
|
+
if (node.source && !node.children?.length && node.isFolder === undefined)
|
|
173
|
+
await setIsFolder(node);
|
|
170
174
|
if (node.children)
|
|
171
175
|
await Promise.allSettled(node.children.map(recur));
|
|
172
176
|
})(data);
|
|
@@ -198,9 +202,18 @@ function getNodeName(node) {
|
|
|
198
202
|
return source.slice(source.lastIndexOf('\\') + 1);
|
|
199
203
|
return base;
|
|
200
204
|
}
|
|
205
|
+
// this is sync
|
|
201
206
|
function nodeIsFolder(node) {
|
|
202
|
-
|
|
203
|
-
|
|
207
|
+
return node.isFolder ?? node.original?.isFolder
|
|
208
|
+
?? (nodeIsLink(node) ? false : (node.children?.length > 0 || !node.source || reconsider()));
|
|
209
|
+
function reconsider() {
|
|
210
|
+
// a networked source may be offline at startup, and become online later: recalculate in the background
|
|
211
|
+
nodeStats(node).then(s => {
|
|
212
|
+
if (s)
|
|
213
|
+
(0, misc_1.setHidden)(node.original || node, { isFolder: s.isDirectory() });
|
|
214
|
+
}, () => { });
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
204
217
|
}
|
|
205
218
|
async function hasDefaultFile(node, ctx) {
|
|
206
219
|
return node.default && nodeIsFolder(node) && await urlToNode(node.default, ctx, node) || undefined;
|
|
@@ -219,7 +232,6 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
|
219
232
|
}
|
|
220
233
|
return ret;
|
|
221
234
|
function getCode() {
|
|
222
|
-
var _a;
|
|
223
235
|
if ((isRoot(node) || node.original) && perm === 'can_delete' // we currently don't allow deleting of vfs nodes from frontend
|
|
224
236
|
|| !node.source && perm === 'can_upload') // Upload possible only if we know where to store. First check node.source because is supposedly faster.
|
|
225
237
|
return const_1.HTTP_FORBIDDEN;
|
|
@@ -231,11 +243,11 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
|
231
243
|
who = node[cur];
|
|
232
244
|
if ((0, misc_1.isWhoObject)(who))
|
|
233
245
|
who = who.this;
|
|
234
|
-
who
|
|
246
|
+
who ??= misc_1.defaultPerms[cur];
|
|
235
247
|
if (typeof who !== 'string' || who === misc_1.WHO_ANY_ACCOUNT)
|
|
236
248
|
break;
|
|
237
249
|
if (!max--) {
|
|
238
|
-
console.error(`endless loop in permission ${perm}=${
|
|
250
|
+
console.error(`endless loop in permission ${perm}=${node[perm] ?? misc_1.defaultPerms[perm]} for ${node.url || getNodeName(node)}`);
|
|
239
251
|
return misc_1.HTTP_SERVER_ERROR;
|
|
240
252
|
}
|
|
241
253
|
cur = who;
|
|
@@ -259,37 +271,35 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
259
271
|
const stream = new node_stream_1.Readable({
|
|
260
272
|
objectMode: true,
|
|
261
273
|
async read() {
|
|
262
|
-
var _a;
|
|
263
274
|
if (started)
|
|
264
275
|
return; // for simplicity, we care about starting, and never suspend
|
|
265
276
|
started = true;
|
|
266
|
-
const {
|
|
277
|
+
const { source } = parent;
|
|
267
278
|
const taken = prefixPath ? undefined : new Set();
|
|
268
279
|
const maskApplier = parentMaskApplier(parent);
|
|
269
280
|
const visitLater = [];
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
281
|
+
const childrenWorking = parent.children?.length && Promise.all(parent.children.map(async (child) => {
|
|
282
|
+
const nodeName = getNodeName(child);
|
|
283
|
+
const name = prefixPath + nodeName;
|
|
284
|
+
taken?.add(normalizeFilename(name));
|
|
285
|
+
const item = { ...child, original: child, name, parent };
|
|
286
|
+
if (await cantSee(item))
|
|
287
|
+
return;
|
|
288
|
+
if (item.source && !item.children?.length) // real items must be accessible, unless there's more to it
|
|
289
|
+
try {
|
|
290
|
+
await promises_1.default.access(item.source);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const isFolder = nodeIsFolder(child);
|
|
296
|
+
if (onlyFiles ? !isFolder : (!onlyFolders || isFolder))
|
|
297
|
+
stream.push(item);
|
|
298
|
+
if (!depth || !isFolder || cantRecur(item))
|
|
299
|
+
return;
|
|
300
|
+
inheritMasks(item, parent);
|
|
301
|
+
visitLater.push([item, name]); // prioritize siblings
|
|
302
|
+
}));
|
|
293
303
|
try {
|
|
294
304
|
if (!source)
|
|
295
305
|
return;
|
|
@@ -299,23 +309,22 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
299
309
|
return;
|
|
300
310
|
try {
|
|
301
311
|
await (0, walkDir_1.walkDir)(source, { depth, ctx, hidden: showHiddenFiles.get(), parallelizeRecursion }, async (entry) => {
|
|
302
|
-
|
|
303
|
-
if (ctx === null || ctx === void 0 ? void 0 : ctx.isAborted()) {
|
|
312
|
+
if (ctx?.isAborted()) {
|
|
304
313
|
stream.push(null);
|
|
305
314
|
return null;
|
|
306
315
|
}
|
|
307
316
|
if ((0, comments_1.usingDescriptIon)() && entry.name === comments_1.DESCRIPT_ION)
|
|
308
317
|
return;
|
|
309
|
-
const { path } = entry;
|
|
318
|
+
const { path } = entry; // this path is not the original deprecated property: we are overwriting/reusing it
|
|
310
319
|
const isFolder = entry.isDirectory();
|
|
311
|
-
let renamed =
|
|
320
|
+
let renamed = parent.rename?.[path];
|
|
312
321
|
if (renamed) {
|
|
313
322
|
const dir = (0, path_1.dirname)(path); // if `path` isn't just the name, copy its dir in renamed
|
|
314
323
|
if (dir !== '.')
|
|
315
324
|
renamed = dir + '/' + renamed;
|
|
316
325
|
}
|
|
317
326
|
const name = prefixPath + (renamed || path);
|
|
318
|
-
if (taken
|
|
327
|
+
if (taken?.has(normalizeFilename(name))) // taken by vfs node above
|
|
319
328
|
return false; // false just in case it's a folder
|
|
320
329
|
const item = { name, isFolder, source: (0, path_1.join)(source, path), parent };
|
|
321
330
|
if (await cantSee(item)) // can't see: don't produce and don't recur
|
|
@@ -331,6 +340,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
331
340
|
}
|
|
332
341
|
}
|
|
333
342
|
finally {
|
|
343
|
+
await childrenWorking;
|
|
334
344
|
for (const [item, name] of visitLater)
|
|
335
345
|
for await (const x of walkNode(item, { depth: depth - 1, prefixPath: name + '/', ctx, requiredPerm, onlyFolders, parallelizeRecursion }))
|
|
336
346
|
stream.push(x);
|
|
@@ -379,13 +389,13 @@ function parentMaskApplier(parent) {
|
|
|
379
389
|
let isFolder = undefined;
|
|
380
390
|
for (const { matcher, mods, mustBeFolder } of matchers) {
|
|
381
391
|
if (mustBeFolder !== undefined) {
|
|
382
|
-
isFolder
|
|
392
|
+
isFolder ??= nodeIsFolder(item);
|
|
383
393
|
if (mustBeFolder !== isFolder)
|
|
384
394
|
continue;
|
|
385
395
|
}
|
|
386
396
|
if (!matcher(virtualBasename))
|
|
387
397
|
continue;
|
|
388
|
-
item.masks
|
|
398
|
+
item.masks &&= lodash_1.default.merge(lodash_1.default.cloneDeep(mods.masks), item.masks); // item.masks must take precedence
|
|
389
399
|
lodash_1.default.defaults(item, mods);
|
|
390
400
|
}
|
|
391
401
|
};
|
|
@@ -421,12 +431,11 @@ function renameUnderPath(rename, path) {
|
|
|
421
431
|
events_1.default.on('accountRenamed', ({ from, to }) => {
|
|
422
432
|
;
|
|
423
433
|
(function renameInNode(n) {
|
|
424
|
-
var _a;
|
|
425
434
|
for (const k of misc_1.PERM_KEYS)
|
|
426
435
|
renameInPerm(n[k]);
|
|
427
436
|
if (n.masks)
|
|
428
437
|
Object.values(n.masks).forEach(renameInNode);
|
|
429
|
-
|
|
438
|
+
n.children?.forEach(renameInNode);
|
|
430
439
|
})(exports.vfs);
|
|
431
440
|
(0, exports.saveVfs)();
|
|
432
441
|
function renameInPerm(a) {
|
package/src/walkDir.js
CHANGED
|
@@ -25,20 +25,19 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
25
25
|
return reject(Error('ENOTDIR'));
|
|
26
26
|
dirQ.add(() => readDir('', depth)
|
|
27
27
|
.then(res => {
|
|
28
|
-
Promise.resolve(res
|
|
28
|
+
Promise.resolve(res?.branchDone).then(resolve);
|
|
29
29
|
}, reject));
|
|
30
30
|
});
|
|
31
31
|
async function readDir(relativePath, depth) {
|
|
32
|
-
var _a, _b;
|
|
33
32
|
if (stopped)
|
|
34
33
|
return;
|
|
35
34
|
const base = (0, path_1.join)(path, relativePath);
|
|
36
35
|
const subDirsDone = [];
|
|
37
36
|
let n = 0;
|
|
38
37
|
let last;
|
|
39
|
-
const res = (
|
|
38
|
+
const res = (await events_1.default.emitAsync('listDiskFolder', { path: base, ctx }))?.[0]; // consider only first result
|
|
40
39
|
const pluginReceiver = lodash_1.default.isFunction(res) && res || null;
|
|
41
|
-
const pluginIterator = lodash_1.default.isFunction(
|
|
40
|
+
const pluginIterator = lodash_1.default.isFunction(res?.[Symbol.asyncIterator] || res?.[Symbol.iterator]) && res;
|
|
42
41
|
if (const_1.IS_WINDOWS && !pluginIterator) { // use native apis to read the 'hidden' attribute
|
|
43
42
|
const direntMethods = {
|
|
44
43
|
isDir: false,
|
|
@@ -67,7 +66,7 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
67
66
|
break;
|
|
68
67
|
if (!hidden && entry.name[0] === '.' && !const_1.IS_WINDOWS)
|
|
69
68
|
continue;
|
|
70
|
-
const stats =
|
|
69
|
+
const stats = entry.isSymbolicLink?.() && await (0, util_files_1.statWithTimeout)((0, path_1.join)(base, entry.name)).catch(() => null);
|
|
71
70
|
if (stats === null)
|
|
72
71
|
continue;
|
|
73
72
|
if (stats)
|
|
@@ -77,7 +76,7 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
77
76
|
expanded.stats = stats;
|
|
78
77
|
await work(expanded);
|
|
79
78
|
}
|
|
80
|
-
pluginReceiver
|
|
79
|
+
pluginReceiver?.(!stopped);
|
|
81
80
|
const branchDone = Promise.allSettled(subDirsDone);
|
|
82
81
|
if (last) // using streams, we don't know when the entries are received, so we need to notify on last item
|
|
83
82
|
last.closingBranch = branchDone.then(() => relativePath);
|
|
@@ -87,7 +86,7 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
87
86
|
return { branchDone, n };
|
|
88
87
|
async function work(entry) {
|
|
89
88
|
entry.path = (relativePath && relativePath + '/') + entry.name;
|
|
90
|
-
pluginReceiver
|
|
89
|
+
pluginReceiver?.(entry);
|
|
91
90
|
if (last && closingQ.length) // pending entries
|
|
92
91
|
last.closingBranch = Promise.resolve(closingQ.shift());
|
|
93
92
|
last = entry;
|
|
@@ -104,9 +103,9 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
104
103
|
const job = () => readDir(entry.path, depth - 1) // recur
|
|
105
104
|
.then(x => x, () => { }) // mute errors
|
|
106
105
|
.then(res => {
|
|
107
|
-
if (!
|
|
106
|
+
if (!res?.n)
|
|
108
107
|
closingQ.push(entry.path); // no children to tell i'm done
|
|
109
|
-
Promise.resolve(res
|
|
108
|
+
Promise.resolve(res?.branchDone).then(() => branchDone.resolve());
|
|
110
109
|
});
|
|
111
110
|
if (parallelizeRecursion)
|
|
112
111
|
dirQ.add(job);
|
|
@@ -119,6 +118,7 @@ const kStats = Symbol('stats');
|
|
|
119
118
|
// Adapting an internal class in Node.js to mimic the behavior of `Dirent` when creating it manually from `Stats`.
|
|
120
119
|
// https://github.com/nodejs/node/blob/a4cf6b204f0b160480153dc293ae748bf15225f9/lib/internal/fs/utils.js#L199C1-L213
|
|
121
120
|
class DirentFromStats extends node_fs_1.Dirent {
|
|
121
|
+
[kStats];
|
|
122
122
|
constructor(name, stats) {
|
|
123
123
|
// @ts-expect-error The constructor has parameters, but they are not represented in types.
|
|
124
124
|
// https://github.com/nodejs/node/blob/a4cf6b204f0b160480153dc293ae748bf15225f9/lib/internal/fs/utils.js#L164
|
package/src/watchLoad.js
CHANGED
|
@@ -37,13 +37,13 @@ function watchLoad(path, parser, { failedOnFirstAttempt, immediateFirst } = {})
|
|
|
37
37
|
void debounced.flush();
|
|
38
38
|
}
|
|
39
39
|
catch (e) {
|
|
40
|
-
retry = setTimeout(install,
|
|
40
|
+
retry = setTimeout(install, 3_000); // manual watching until watch is successful
|
|
41
41
|
if (first)
|
|
42
|
-
failedOnFirstAttempt
|
|
42
|
+
failedOnFirstAttempt?.();
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function unwatch() {
|
|
46
|
-
watcher
|
|
46
|
+
watcher?.close();
|
|
47
47
|
clearTimeout(retry);
|
|
48
48
|
watcher = undefined;
|
|
49
49
|
}
|
|
@@ -52,7 +52,7 @@ function watchLoad(path, parser, { failedOnFirstAttempt, immediateFirst } = {})
|
|
|
52
52
|
return;
|
|
53
53
|
doing = true;
|
|
54
54
|
try {
|
|
55
|
-
const text = await (0, util_files_1.
|
|
55
|
+
const text = await (0, util_files_1.readFileWithBusyRetry)(path).catch(e => {
|
|
56
56
|
if (e.code === 'EPERM')
|
|
57
57
|
console.error("missing permissions on file", path); // warn user, who could be clueless about this problem
|
|
58
58
|
return '';
|
package/src/zip.js
CHANGED
|
@@ -14,14 +14,13 @@ const api_get_file_list_1 = require("./api.get_file_list");
|
|
|
14
14
|
const comments_1 = require("./comments");
|
|
15
15
|
// expects 'node' to have had permissions checked by caller
|
|
16
16
|
async function zipStreamFromFolder(node, ctx) {
|
|
17
|
-
|
|
18
|
-
const list = (_a = (0, misc_1.wantArray)(ctx.query.list)[0]) === null || _a === void 0 ? void 0 : _a.split('//'); // slash is the only char not allowed in file names both for windows and unix, but still we need to encode whole paths, so the only safe choice to separate the entries is the double slash
|
|
17
|
+
const list = (0, misc_1.wantArray)(ctx.query.list)[0]?.split('//'); // slash is the only char not allowed in file names both for windows and unix, but still we need to encode whole paths, so the only safe choice to separate the entries is the double slash
|
|
19
18
|
if (!list && (0, vfs_1.statusCodeForMissingPerm)(node, 'can_archive', ctx))
|
|
20
19
|
return;
|
|
21
20
|
ctx.status = const_1.HTTP_OK;
|
|
22
21
|
ctx.mime = 'zip';
|
|
23
22
|
// ctx.query.list is undefined | string | string[]
|
|
24
|
-
const name =
|
|
23
|
+
const name = list?.length === 1 ? (0, misc_1.safeDecodeURIComponent)((0, path_1.basename)(list[0])) : (0, vfs_1.getNodeName)(node);
|
|
25
24
|
(0, serveFile_1.forceDownload)(ctx, ((0, misc_1.isWindowsDrive)(name) ? name[0] : (name || 'archive')) + '.zip');
|
|
26
25
|
const { filterName, filterComment } = (0, api_get_file_list_1.paramsToFilter)(ctx.query);
|
|
27
26
|
const walker = !list ? (0, vfs_1.walkNode)(node, { ctx, requiredPerm: 'can_archive' })
|
|
@@ -61,7 +60,7 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
61
60
|
return { path: name + '/' };
|
|
62
61
|
if (!source)
|
|
63
62
|
return;
|
|
64
|
-
const st = el.stats ||
|
|
63
|
+
const st = await (el.stats || (0, misc_1.statWithTimeout)(source));
|
|
65
64
|
if (!st || !st.isFile())
|
|
66
65
|
return;
|
|
67
66
|
return {
|
|
@@ -73,7 +72,7 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
73
72
|
getData: () => (0, fs_1.createReadStream)(source, { start: 0, end: Math.max(0, st.size - 1) })
|
|
74
73
|
};
|
|
75
74
|
}
|
|
76
|
-
catch
|
|
75
|
+
catch { }
|
|
77
76
|
});
|
|
78
77
|
const zip = new QuickZipStream_1.QuickZipStream(mappedWalker);
|
|
79
78
|
const time = 1000 * zipSeconds.get();
|
|
@@ -86,6 +85,6 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
86
85
|
ctx.body = zip;
|
|
87
86
|
ctx.req.on('close', () => zip.destroy());
|
|
88
87
|
ctx.state.archive = 'zip';
|
|
89
|
-
(0, serveFile_1.monitorAsDownload)(ctx, size, range
|
|
88
|
+
(0, serveFile_1.monitorAsDownload)(ctx, size, range?.start);
|
|
90
89
|
}
|
|
91
90
|
const zipSeconds = (0, config_1.defineConfig)('zip_calculate_size_for_seconds', 5);
|