hfs 0.26.9 → 0.29.0
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/README.md +17 -3
- package/admin/assets/index-cbb42a0e.js +415 -0
- package/admin/assets/index-f8049da8.css +1 -0
- package/{frontend/assets/sha512.6af42937.js → admin/assets/sha512-3273321f.js} +2 -2
- package/admin/index.html +2 -2
- package/frontend/assets/index-72e96bb2.js +85 -0
- package/frontend/assets/index-cbcc6ac5.css +1 -0
- package/{admin/assets/sha512.9dfe82e1.js → frontend/assets/sha512-2c2fa926.js} +2 -2
- package/frontend/index.html +3 -3
- package/package.json +8 -10
- package/plugins/vhosting/plugin.js +23 -20
- package/src/QuickZipStream.js +2 -25
- package/src/ThrottledStream.js +1 -1
- package/src/adminApis.js +6 -8
- package/src/api.accounts.js +10 -10
- package/src/api.auth.js +21 -17
- package/src/api.file_list.js +13 -6
- package/src/api.helpers.js +6 -6
- package/src/api.monitor.js +2 -0
- package/src/api.plugins.js +1 -0
- package/src/api.vfs.js +17 -19
- package/src/apiMiddleware.js +16 -9
- package/src/block.js +1 -0
- package/src/commands.js +1 -0
- package/src/config.js +3 -2
- package/src/connections.js +1 -1
- package/src/const.js +19 -7
- package/src/crypt.js +1 -1
- package/src/debounceAsync.js +1 -0
- package/src/events.js +1 -1
- package/src/frontEndApis.js +23 -2
- package/src/github.js +5 -1
- package/src/index.js +5 -3
- package/src/listen.js +6 -3
- package/src/log.js +6 -6
- package/src/middlewares.js +32 -26
- package/src/misc.js +27 -2
- package/src/perm.js +31 -29
- package/src/plugins.js +6 -8
- package/src/serveFile.js +15 -13
- package/src/serveGuiFiles.js +5 -28
- package/src/sse.js +3 -2
- package/src/throttler.js +1 -1
- package/src/update.js +3 -2
- package/src/upload.js +92 -0
- package/src/util-files.js +19 -13
- package/src/util-generators.js +1 -0
- package/src/util-http.js +3 -1
- package/src/util-os.js +41 -0
- package/src/vfs.js +44 -37
- package/src/watchLoad.js +1 -1
- package/src/zip.js +9 -6
- package/admin/assets/index.bb5198ec.js +0 -281
- package/admin/assets/index.dcc78777.css +0 -1
- package/frontend/assets/index.27a78796.js +0 -85
- package/frontend/assets/index.93366732.css +0 -1
package/src/vfs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
@@ -21,6 +21,7 @@ const WHO_ANY_ACCOUNT = '*';
|
|
|
21
21
|
exports.defaultPerms = {
|
|
22
22
|
can_see: WHO_ANYONE,
|
|
23
23
|
can_read: WHO_ANYONE,
|
|
24
|
+
can_upload: WHO_NO_ONE,
|
|
24
25
|
};
|
|
25
26
|
exports.MIME_AUTO = 'auto';
|
|
26
27
|
function inheritFromParent(parent, child) {
|
|
@@ -36,7 +37,7 @@ function inheritFromParent(parent, child) {
|
|
|
36
37
|
child.mime || (child.mime = parent.mime);
|
|
37
38
|
return child;
|
|
38
39
|
}
|
|
39
|
-
async function urlToNode(url, ctx, parent = exports.vfs) {
|
|
40
|
+
async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
40
41
|
var _a;
|
|
41
42
|
let initialSlashes = 0;
|
|
42
43
|
while (url[initialSlashes] === '/')
|
|
@@ -48,25 +49,23 @@ async function urlToNode(url, ctx, parent = exports.vfs) {
|
|
|
48
49
|
const rest = nextSlash < 0 ? '' : url.slice(nextSlash + 1, url.endsWith('/') ? -1 : undefined);
|
|
49
50
|
if ((0, misc_1.dirTraversal)(name) || /[\/]/.test(name)) {
|
|
50
51
|
if (ctx)
|
|
51
|
-
ctx.status =
|
|
52
|
+
ctx.status = const_1.HTTP_FOOL;
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
+
// does the tree node have a child that goes by this name?
|
|
56
|
+
const sameName = !const_1.IS_WINDOWS ? (x) => x === name // easy
|
|
57
|
+
: (0, misc_2.with_)(name.toLowerCase(), lc => (x) => x.toLowerCase() === lc);
|
|
58
|
+
const child = (_a = parent.children) === null || _a === void 0 ? void 0 : _a.find(x => sameName(getNodeName(x)));
|
|
55
59
|
const ret = {
|
|
60
|
+
...child,
|
|
61
|
+
original: child,
|
|
56
62
|
isTemp: true,
|
|
57
|
-
url: (0, misc_1.enforceFinal)('/', parent.url || '') + name,
|
|
58
|
-
parents,
|
|
59
63
|
};
|
|
60
|
-
parents.push(parent);
|
|
61
64
|
inheritFromParent(parent, ret);
|
|
62
65
|
inheritMasks(ret, parent, name);
|
|
63
66
|
applyMasks(ret, parent, name);
|
|
64
|
-
// does the tree node have a child that goes by this name?
|
|
65
|
-
const sameName = !const_1.IS_WINDOWS ? (x) => x === name // easy
|
|
66
|
-
: (0, misc_2.with_)(name.toLowerCase(), lc => (x) => x.toLowerCase() === lc);
|
|
67
|
-
const child = (_a = parent.children) === null || _a === void 0 ? void 0 : _a.find(x => sameName(getNodeName(x)));
|
|
68
67
|
if (child) // yes
|
|
69
|
-
return urlToNode(rest, ctx,
|
|
68
|
+
return urlToNode(rest, ctx, ret, getRest);
|
|
70
69
|
// not in the tree, we can see consider continuing on the disk
|
|
71
70
|
if (!parent.source)
|
|
72
71
|
return; // but then we need the current node to be linked to the disk, otherwise, we give up
|
|
@@ -83,13 +82,16 @@ async function urlToNode(url, ctx, parent = exports.vfs) {
|
|
|
83
82
|
if (parent.default)
|
|
84
83
|
inheritFromParent({ mime: { '*': exports.MIME_AUTO } }, ret);
|
|
85
84
|
if (rest)
|
|
86
|
-
return urlToNode(rest, ctx, ret);
|
|
85
|
+
return urlToNode(rest, ctx, ret, getRest);
|
|
87
86
|
if (ret.source)
|
|
88
87
|
try {
|
|
89
88
|
await promises_1.default.stat(ret.source);
|
|
90
89
|
} // check existence
|
|
91
90
|
catch (_b) {
|
|
92
|
-
|
|
91
|
+
if (!getRest)
|
|
92
|
+
return;
|
|
93
|
+
getRest(onDisk);
|
|
94
|
+
return parent;
|
|
93
95
|
}
|
|
94
96
|
return ret;
|
|
95
97
|
}
|
|
@@ -101,11 +103,17 @@ function saveVfs() {
|
|
|
101
103
|
}
|
|
102
104
|
exports.saveVfs = saveVfs;
|
|
103
105
|
function getNodeName(node) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
const { name, source: s } = node;
|
|
107
|
+
if (name)
|
|
108
|
+
return name;
|
|
109
|
+
if (!s)
|
|
110
|
+
return ''; // should happen only for root
|
|
111
|
+
if (/^[a-zA-Z]:\\?$/.test(s))
|
|
112
|
+
return s.slice(0, 2); // exclude trailing slash
|
|
113
|
+
const base = (0, path_1.basename)(s);
|
|
114
|
+
if (/^[./\\]*$/.test(base)) // if empty or special-chars-only
|
|
115
|
+
return (0, path_1.basename)((0, path_1.resolve)(s)); // resolve to try to get more
|
|
116
|
+
return base;
|
|
109
117
|
}
|
|
110
118
|
exports.getNodeName = getNodeName;
|
|
111
119
|
async function nodeIsDirectory(node) {
|
|
@@ -115,18 +123,20 @@ exports.nodeIsDirectory = nodeIsDirectory;
|
|
|
115
123
|
function hasPermission(node, perm, ctx) {
|
|
116
124
|
var _a;
|
|
117
125
|
return matchWho((_a = node[perm]) !== null && _a !== void 0 ? _a : exports.defaultPerms[perm], ctx)
|
|
118
|
-
&& (perm !== 'can_see' || hasPermission(node, 'can_read', ctx)); //
|
|
126
|
+
&& (perm !== 'can_see' || hasPermission(node, 'can_read', ctx)); // can_see is used to hide something you nonetheless can_read, so you MUST also can_read
|
|
119
127
|
}
|
|
120
128
|
exports.hasPermission = hasPermission;
|
|
121
129
|
async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
|
|
122
130
|
var _a;
|
|
123
131
|
const { children, source } = parent;
|
|
132
|
+
const took = prefixPath ? undefined : new Set();
|
|
124
133
|
if (children)
|
|
125
|
-
for (
|
|
126
|
-
const
|
|
134
|
+
for (const child of children) {
|
|
135
|
+
const name = prefixPath + getNodeName(child);
|
|
136
|
+
took === null || took === void 0 ? void 0 : took.add(name);
|
|
127
137
|
yield* workItem({
|
|
128
138
|
...child,
|
|
129
|
-
name
|
|
139
|
+
name,
|
|
130
140
|
}, depth > 0 && await nodeIsDirectory(child).catch(() => false));
|
|
131
141
|
}
|
|
132
142
|
if (!source)
|
|
@@ -135,9 +145,11 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
|
|
|
135
145
|
for await (const path of (0, misc_1.dirStream)(source, depth)) {
|
|
136
146
|
if (ctx === null || ctx === void 0 ? void 0 : ctx.req.aborted)
|
|
137
147
|
return;
|
|
138
|
-
const
|
|
148
|
+
const name = prefixPath + (((_a = parent.rename) === null || _a === void 0 ? void 0 : _a[path]) || path);
|
|
149
|
+
if (took === null || took === void 0 ? void 0 : took.has(name))
|
|
150
|
+
continue;
|
|
139
151
|
yield* workItem({
|
|
140
|
-
name
|
|
152
|
+
name,
|
|
141
153
|
source: (0, path_1.join)(source, path),
|
|
142
154
|
rename: renameUnderPath(parent.rename, path),
|
|
143
155
|
});
|
|
@@ -151,12 +163,7 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '') {
|
|
|
151
163
|
const name = getNodeName(item);
|
|
152
164
|
// we basename for depth>0 where we already have the rest of the path in the parent's url, and would be duplicated
|
|
153
165
|
const virtualBasename = (0, path_1.basename)(name);
|
|
154
|
-
|
|
155
|
-
Object.assign(item, {
|
|
156
|
-
isTemp: true,
|
|
157
|
-
url,
|
|
158
|
-
parents: [...parent.parents || [], parent],
|
|
159
|
-
});
|
|
166
|
+
item.isTemp = true;
|
|
160
167
|
inheritFromParent(parent, item);
|
|
161
168
|
applyMasks(item, parent, virtualBasename);
|
|
162
169
|
if (ctx && !hasPermission(item, 'can_see', ctx))
|
|
@@ -183,11 +190,11 @@ function inheritMasks(item, parent, virtualBasename) {
|
|
|
183
190
|
if (!masks)
|
|
184
191
|
return;
|
|
185
192
|
const o = {};
|
|
186
|
-
for (const k
|
|
193
|
+
for (const [k, v] of Object.entries(masks))
|
|
187
194
|
if (k.startsWith('**/'))
|
|
188
|
-
o[k.slice(3)] =
|
|
195
|
+
o[k.slice(3)] = v;
|
|
189
196
|
else if (k.startsWith(virtualBasename + '/'))
|
|
190
|
-
o[k.slice(virtualBasename.length + 1)] =
|
|
197
|
+
o[k.slice(virtualBasename.length + 1)] = v;
|
|
191
198
|
if (Object.keys(o).length)
|
|
192
199
|
item.masks = o;
|
|
193
200
|
}
|
|
@@ -206,7 +213,7 @@ function matchWho(who, ctx) {
|
|
|
206
213
|
(0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.getCurrentUsernameExpanded)(ctx)).some((u) => who.includes(u)))();
|
|
207
214
|
}
|
|
208
215
|
function cantReadStatusCode(node) {
|
|
209
|
-
return node.can_read === false ? const_1.
|
|
216
|
+
return node.can_read === false ? const_1.HTTP_FORBIDDEN : const_1.HTTP_UNAUTHORIZED;
|
|
210
217
|
}
|
|
211
218
|
exports.cantReadStatusCode = cantReadStatusCode;
|
|
212
219
|
events_1.default.on('accountRenamed', (from, to) => {
|
|
@@ -214,8 +221,8 @@ events_1.default.on('accountRenamed', (from, to) => {
|
|
|
214
221
|
saveVfs();
|
|
215
222
|
function recur(n) {
|
|
216
223
|
var _a;
|
|
217
|
-
|
|
218
|
-
|
|
224
|
+
for (const k of (0, misc_1.typedKeys)(exports.defaultPerms))
|
|
225
|
+
replace(n[k]);
|
|
219
226
|
if (n.masks)
|
|
220
227
|
Object.values(n.masks).forEach(recur);
|
|
221
228
|
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach(recur);
|
package/src/watchLoad.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
package/src/zip.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
@@ -13,16 +13,19 @@ const promises_1 = __importDefault(require("fs/promises"));
|
|
|
13
13
|
const config_1 = require("./config");
|
|
14
14
|
const path_1 = require("path");
|
|
15
15
|
const serveFile_1 = require("./serveFile");
|
|
16
|
+
const const_1 = require("./const");
|
|
16
17
|
async function zipStreamFromFolder(node, ctx) {
|
|
17
|
-
|
|
18
|
+
var _a;
|
|
19
|
+
ctx.status = const_1.HTTP_OK;
|
|
18
20
|
ctx.mime = 'zip';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// ctx.query.list is undefined | string | string[]
|
|
22
|
+
const list = (_a = (0, misc_1.wantArray)(ctx.query.list)[0]) === null || _a === void 0 ? void 0 : _a.split('*'); // we are using * as separator because it cannot be used in a file name and doesn't need url encoding
|
|
23
|
+
const name = (list === null || list === void 0 ? void 0 : list.length) === 1 ? (0, path_1.basename)(list[0]) : (0, vfs_1.getNodeName)(node);
|
|
24
|
+
ctx.attachment(((0, misc_1.isWindowsDrive)(name) ? name[0] : (name || 'archive')) + '.zip');
|
|
21
25
|
const filter = (0, misc_1.pattern2filter)(String(ctx.query.search || ''));
|
|
22
|
-
const { list } = ctx.query;
|
|
23
26
|
const walker = !list ? (0, vfs_1.walkNode)(node, ctx, Infinity)
|
|
24
27
|
: (async function* () {
|
|
25
|
-
for await (const el of
|
|
28
|
+
for await (const el of list) {
|
|
26
29
|
const subNode = await (0, vfs_1.urlToNode)(el, ctx, node);
|
|
27
30
|
if (!subNode || !(0, vfs_1.hasPermission)(subNode, 'can_read', ctx))
|
|
28
31
|
continue;
|