hfs 0.43.0 → 0.44.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 +50 -9
- package/admin/assets/index-35f6e3dc.css +1 -0
- package/admin/assets/index-ef68a7ab.js +510 -0
- package/{frontend/assets/sha512-8ebf6e2a.js → admin/assets/sha512-d091720e.js} +1 -1
- package/admin/index.html +2 -2
- package/frontend/assets/index-a3b0d6ac.js +94 -0
- package/frontend/assets/index-fe0f3d77.css +1 -0
- package/{admin/assets/sha512-55ff2fa3.js → frontend/assets/sha512-96fd938f.js} +1 -1
- package/frontend/index.html +3 -2
- package/package.json +2 -2
- package/plugins/download-counter/plugin.js +5 -3
- package/plugins/download-counter/public/main.js +2 -2
- package/plugins/vhosting/plugin.js +17 -11
- package/src/adminApis.js +1 -1
- package/src/api.auth.js +4 -1
- package/src/api.file_list.js +7 -8
- package/src/api.lang.js +2 -1
- package/src/api.vfs.js +41 -28
- package/src/apiMiddleware.js +3 -2
- package/src/const.js +4 -3
- package/src/customHtml.js +1 -1
- package/src/debounceAsync.js +3 -3
- package/src/frontEndApis.js +6 -6
- package/src/github.js +26 -8
- package/src/index.js +2 -1
- package/src/lang.js +9 -19
- package/src/langs/embedded.js +13 -0
- package/src/langs/hfs-lang-ms.json +70 -0
- package/src/langs/hfs-lang-ru.json +22 -22
- package/src/langs/hfs-lang-zh-tw.json +106 -0
- package/src/listen.js +1 -1
- package/src/log.js +6 -2
- package/src/middlewares.js +14 -18
- package/src/misc.js +7 -3
- package/src/plugins.js +6 -6
- package/src/serveFile.js +1 -1
- package/src/serveGuiFiles.js +2 -2
- package/src/update.js +1 -2
- package/src/upload.js +6 -6
- package/src/util-http.js +5 -3
- package/src/vfs.js +100 -52
- package/src/zip.js +4 -4
- package/admin/assets/index-5cd667a5.js +0 -511
- package/admin/assets/index-8ff39373.css +0 -1
- package/frontend/assets/index-27488fde.js +0 -94
- package/frontend/assets/index-54a5c76f.css +0 -1
package/src/vfs.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.masksCouldGivePermission = exports.walkNode = exports.statusCodeForMissingPerm = exports.hasPermission = exports.nodeIsDirectory = exports.getNodeName = exports.saveVfs = exports.vfs = exports.urlToNode = exports.isSameFilenameAs = exports.MIME_AUTO = exports.defaultPerms = exports.WHO_ANY_ACCOUNT = exports.WHO_NO_ONE = exports.WHO_ANYONE = void 0;
|
|
7
|
+
exports.parentMaskApplier = exports.masksCouldGivePermission = exports.walkNode = exports.statusCodeForMissingPerm = exports.hasPermission = exports.nodeIsDirectory = exports.getNodeName = exports.saveVfs = exports.vfs = exports.urlToNode = exports.applyParentToChild = exports.isSameFilenameAs = exports.MIME_AUTO = exports.PERM_KEYS = exports.defaultPerms = exports.WHO_ANY_ACCOUNT = exports.WHO_NO_ONE = exports.WHO_ANYONE = void 0;
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
9
|
const path_1 = require("path");
|
|
10
10
|
const misc_1 = require("./misc");
|
|
@@ -17,17 +17,21 @@ exports.WHO_ANYONE = true;
|
|
|
17
17
|
exports.WHO_NO_ONE = false;
|
|
18
18
|
exports.WHO_ANY_ACCOUNT = '*';
|
|
19
19
|
exports.defaultPerms = {
|
|
20
|
-
can_see:
|
|
20
|
+
can_see: 'can_read',
|
|
21
21
|
can_read: exports.WHO_ANYONE,
|
|
22
|
-
can_list:
|
|
22
|
+
can_list: 'can_read',
|
|
23
23
|
can_upload: exports.WHO_NO_ONE,
|
|
24
24
|
can_delete: exports.WHO_NO_ONE,
|
|
25
25
|
};
|
|
26
|
+
exports.PERM_KEYS = (0, misc_1.typedKeys)(exports.defaultPerms);
|
|
26
27
|
exports.MIME_AUTO = 'auto';
|
|
27
28
|
function inheritFromParent(parent, child) {
|
|
28
29
|
var _a, _b, _c;
|
|
29
|
-
for (const k of (0, misc_1.typedKeys)(exports.defaultPerms))
|
|
30
|
-
|
|
30
|
+
for (const k of (0, misc_1.typedKeys)(exports.defaultPerms)) {
|
|
31
|
+
const v = parent[k];
|
|
32
|
+
if (v !== undefined) // small optimization: don't expand the object
|
|
33
|
+
(_a = child[k]) !== null && _a !== void 0 ? _a : (child[k] = v);
|
|
34
|
+
}
|
|
31
35
|
if (typeof parent.mime === 'object' && typeof child.mime === 'object')
|
|
32
36
|
lodash_1.default.defaults(child.mime, parent.mime);
|
|
33
37
|
else
|
|
@@ -40,6 +44,20 @@ function isSameFilenameAs(name) {
|
|
|
40
44
|
return (other) => lc === (typeof other === 'string' ? other : getNodeName(other)).toLowerCase();
|
|
41
45
|
}
|
|
42
46
|
exports.isSameFilenameAs = isSameFilenameAs;
|
|
47
|
+
function applyParentToChild(child, parent, name) {
|
|
48
|
+
const ret = {
|
|
49
|
+
...child,
|
|
50
|
+
original: child,
|
|
51
|
+
isTemp: true,
|
|
52
|
+
parent,
|
|
53
|
+
};
|
|
54
|
+
name || (name = child ? getNodeName(child) : '');
|
|
55
|
+
inheritMasks(ret, parent, name);
|
|
56
|
+
parentMaskApplier(parent)(ret, name);
|
|
57
|
+
inheritFromParent(parent, ret);
|
|
58
|
+
return ret;
|
|
59
|
+
}
|
|
60
|
+
exports.applyParentToChild = applyParentToChild;
|
|
43
61
|
async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
44
62
|
var _a;
|
|
45
63
|
let initialSlashes = 0;
|
|
@@ -57,19 +75,11 @@ async function urlToNode(url, ctx, parent = exports.vfs, getRest) {
|
|
|
57
75
|
}
|
|
58
76
|
// does the tree node have a child that goes by this name?
|
|
59
77
|
const child = (_a = parent.children) === null || _a === void 0 ? void 0 : _a.find(isSameFilenameAs(name));
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
isTemp: true,
|
|
64
|
-
};
|
|
65
|
-
inheritMasks(ret, parent, name);
|
|
66
|
-
applyMasks(ret, parent, name);
|
|
67
|
-
inheritFromParent(parent, ret);
|
|
78
|
+
if (!child && !parent.source)
|
|
79
|
+
return; // on tree or on disk
|
|
80
|
+
const ret = applyParentToChild(child, parent, name);
|
|
68
81
|
if (child) // yes
|
|
69
82
|
return urlToNode(rest, ctx, ret, getRest);
|
|
70
|
-
// not in the tree, we can see consider continuing on the disk
|
|
71
|
-
if (!parent.source)
|
|
72
|
-
return; // but then we need the current node to be linked to the disk, otherwise, we give up
|
|
73
83
|
let onDisk = name;
|
|
74
84
|
if (parent.rename) { // reverse the mapping
|
|
75
85
|
for (const [from, to] of Object.entries(parent.rename))
|
|
@@ -124,15 +134,38 @@ async function nodeIsDirectory(node) {
|
|
|
124
134
|
}
|
|
125
135
|
exports.nodeIsDirectory = nodeIsDirectory;
|
|
126
136
|
function hasPermission(node, perm, ctx) {
|
|
127
|
-
|
|
128
|
-
return (node.source || perm !== 'can_upload') // Upload possible only if we know where to store. First check node.source because is supposedly faster.
|
|
129
|
-
&& matchWho((_a = node[perm]) !== null && _a !== void 0 ? _a : exports.defaultPerms[perm], ctx);
|
|
137
|
+
return !statusCodeForMissingPerm(node, perm, ctx, false);
|
|
130
138
|
}
|
|
131
139
|
exports.hasPermission = hasPermission;
|
|
132
|
-
function statusCodeForMissingPerm(node, perm, ctx) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
140
|
+
function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
141
|
+
const ret = getCode();
|
|
142
|
+
if (ret && assign)
|
|
143
|
+
ctx.status = ret;
|
|
144
|
+
return ret;
|
|
145
|
+
function getCode() {
|
|
146
|
+
var _a;
|
|
147
|
+
if (!node.source && perm === 'can_upload') // Upload possible only if we know where to store. First check node.source because is supposedly faster.
|
|
148
|
+
return const_1.HTTP_FORBIDDEN;
|
|
149
|
+
// calculate value of permission resolving references to other permissions, avoiding infinite loop
|
|
150
|
+
let who;
|
|
151
|
+
let max = exports.PERM_KEYS.length;
|
|
152
|
+
do {
|
|
153
|
+
who = (_a = node[perm]) !== null && _a !== void 0 ? _a : exports.defaultPerms[perm];
|
|
154
|
+
if (!max-- || typeof who !== 'string' || who === exports.WHO_ANY_ACCOUNT)
|
|
155
|
+
break;
|
|
156
|
+
perm = who;
|
|
157
|
+
} while (1);
|
|
158
|
+
if (Array.isArray(who)) {
|
|
159
|
+
const arr = who; // shut up ts
|
|
160
|
+
// check if I or any ancestor match `who`, but cache ancestors' usernames inside context state
|
|
161
|
+
const some = (0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.getCurrentUsernameExpanded)(ctx))
|
|
162
|
+
.some((u) => arr.includes(u));
|
|
163
|
+
return some ? 0 : const_1.HTTP_UNAUTHORIZED;
|
|
164
|
+
}
|
|
165
|
+
return typeof who === 'boolean' ? (who ? 0 : const_1.HTTP_FORBIDDEN)
|
|
166
|
+
: who === exports.WHO_ANY_ACCOUNT ? (ctx.state.account ? 0 : const_1.HTTP_UNAUTHORIZED)
|
|
167
|
+
: (() => { throw Error('invalid permission: ' + who); })();
|
|
168
|
+
}
|
|
136
169
|
}
|
|
137
170
|
exports.statusCodeForMissingPerm = statusCodeForMissingPerm;
|
|
138
171
|
// it's responsibility of the caller to verify you have list permission on parent, as callers have different needs.
|
|
@@ -141,6 +174,7 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '', requiredPerm)
|
|
|
141
174
|
var _a;
|
|
142
175
|
const { children, source } = parent;
|
|
143
176
|
const took = prefixPath ? undefined : new Set();
|
|
177
|
+
const maskApplier = parentMaskApplier(parent);
|
|
144
178
|
if (children)
|
|
145
179
|
for (const child of children) {
|
|
146
180
|
const nodeName = getNodeName(child);
|
|
@@ -196,7 +230,7 @@ async function* walkNode(parent, ctx, depth = 0, prefixPath = '', requiredPerm)
|
|
|
196
230
|
// item will be changed, so be sure to pass a temp node
|
|
197
231
|
function canSee(item) {
|
|
198
232
|
// we basename for depth>0 where we already have the rest of the path in the parent's url, and would be duplicated
|
|
199
|
-
|
|
233
|
+
maskApplier(item, (0, path_1.basename)(getNodeName(item)));
|
|
200
234
|
inheritFromParent(parent, item);
|
|
201
235
|
if (ctx && !hasPermission(item, 'can_see', ctx))
|
|
202
236
|
return;
|
|
@@ -209,25 +243,45 @@ function masksCouldGivePermission(masks, perm) {
|
|
|
209
243
|
return masks !== undefined && Object.values(masks).some(props => props[perm] || masksCouldGivePermission(props.masks, perm));
|
|
210
244
|
}
|
|
211
245
|
exports.masksCouldGivePermission = masksCouldGivePermission;
|
|
212
|
-
function
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
246
|
+
function parentMaskApplier(parent) {
|
|
247
|
+
const matchers = Object.entries(parent.masks || {}).map(([k, v]) => {
|
|
248
|
+
k = k.startsWith('**/') ? k.slice(3) : !k.includes('/') ? k : '';
|
|
249
|
+
if (!k)
|
|
250
|
+
return;
|
|
251
|
+
const m = (0, misc_1.makeMatcher)(k);
|
|
252
|
+
return [m, v];
|
|
253
|
+
});
|
|
254
|
+
return (item, virtualBasename) => {
|
|
255
|
+
if (virtualBasename === undefined)
|
|
256
|
+
virtualBasename = getNodeName(item);
|
|
257
|
+
for (const entry of matchers) {
|
|
258
|
+
if (!entry)
|
|
259
|
+
continue;
|
|
260
|
+
const [matcher, mods] = entry;
|
|
261
|
+
if (!matcher(virtualBasename))
|
|
262
|
+
continue;
|
|
263
|
+
if (item.masks)
|
|
264
|
+
item.masks = lodash_1.default.merge(lodash_1.default.cloneDeep(mods.masks), item.masks); // item.masks must take precedence
|
|
265
|
+
lodash_1.default.defaults(item, mods);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
220
268
|
}
|
|
269
|
+
exports.parentMaskApplier = parentMaskApplier;
|
|
221
270
|
function inheritMasks(item, parent, virtualBasename) {
|
|
222
271
|
const { masks } = parent;
|
|
223
272
|
if (!masks)
|
|
224
273
|
return;
|
|
225
274
|
const o = {};
|
|
226
|
-
for (const [k, v] of Object.entries(masks))
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
o[k
|
|
275
|
+
for (const [k, v] of Object.entries(masks)) {
|
|
276
|
+
const neg = k[0] === '!' && k[1] !== '(' ? '!' : '';
|
|
277
|
+
const withoutNeg = neg ? k.slice(1) : k;
|
|
278
|
+
if (withoutNeg.startsWith('**'))
|
|
279
|
+
o[k] = v;
|
|
280
|
+
else if (withoutNeg.startsWith('*/'))
|
|
281
|
+
o[neg + withoutNeg.slice(2)] = v;
|
|
282
|
+
else if (withoutNeg.startsWith(virtualBasename + '/'))
|
|
283
|
+
o[neg + withoutNeg.slice(virtualBasename.length + 1)] = v;
|
|
284
|
+
}
|
|
231
285
|
if (Object.keys(o).length)
|
|
232
286
|
item.masks = lodash_1.default.defaults(item.masks, o);
|
|
233
287
|
}
|
|
@@ -239,24 +293,18 @@ function renameUnderPath(rename, path) {
|
|
|
239
293
|
delete rename[''];
|
|
240
294
|
return lodash_1.default.isEmpty(rename) ? undefined : rename;
|
|
241
295
|
}
|
|
242
|
-
function matchWho(who, ctx) {
|
|
243
|
-
return who === exports.WHO_ANYONE
|
|
244
|
-
|| who === exports.WHO_ANY_ACCOUNT && Boolean(ctx.state.account)
|
|
245
|
-
|| Array.isArray(who) // check if I or any ancestor match `who`, but cache ancestors' usernames inside context state
|
|
246
|
-
&& (0, misc_1.getOrSet)(ctx.state, 'usernames', () => (0, perm_1.getCurrentUsernameExpanded)(ctx)).some((u) => who.includes(u));
|
|
247
|
-
}
|
|
248
296
|
events_1.default.on('accountRenamed', (from, to) => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
function recur(n) {
|
|
297
|
+
;
|
|
298
|
+
(function renameInNode(n) {
|
|
252
299
|
var _a;
|
|
253
|
-
for (const k of
|
|
254
|
-
|
|
300
|
+
for (const k of exports.PERM_KEYS)
|
|
301
|
+
renameInPerm(n[k]);
|
|
255
302
|
if (n.masks)
|
|
256
|
-
Object.values(n.masks).forEach(
|
|
257
|
-
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach(
|
|
258
|
-
}
|
|
259
|
-
|
|
303
|
+
Object.values(n.masks).forEach(renameInNode);
|
|
304
|
+
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach(renameInNode);
|
|
305
|
+
})(exports.vfs);
|
|
306
|
+
saveVfs();
|
|
307
|
+
function renameInPerm(a) {
|
|
260
308
|
if (!Array.isArray(a))
|
|
261
309
|
return;
|
|
262
310
|
for (let i = 0; i < a.length; i++)
|
package/src/zip.js
CHANGED
|
@@ -26,16 +26,16 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
26
26
|
const filter = (0, misc_1.pattern2filter)(String(ctx.query.search || ''));
|
|
27
27
|
const walker = !list ? (0, vfs_1.walkNode)(node, ctx, Infinity, '', 'can_read')
|
|
28
28
|
: (async function* () {
|
|
29
|
-
for await (const
|
|
30
|
-
const subNode = await (0, vfs_1.urlToNode)(
|
|
29
|
+
for await (const uri of list) {
|
|
30
|
+
const subNode = await (0, vfs_1.urlToNode)(uri, ctx, node);
|
|
31
31
|
if (!subNode)
|
|
32
32
|
continue;
|
|
33
33
|
if (await (0, vfs_1.nodeIsDirectory)(subNode)) { // a directory needs to walked
|
|
34
34
|
if ((0, vfs_1.hasPermission)(subNode, 'can_list', ctx))
|
|
35
|
-
yield* (0, vfs_1.walkNode)(subNode, ctx, Infinity,
|
|
35
|
+
yield* (0, vfs_1.walkNode)(subNode, ctx, Infinity, uri + '/', 'can_read');
|
|
36
36
|
continue;
|
|
37
37
|
}
|
|
38
|
-
let folder = (0, path_1.dirname)(
|
|
38
|
+
let folder = (0, path_1.dirname)(decodeURIComponent(uri)); // decodeURI() won't account for %23=#
|
|
39
39
|
folder = folder === '.' ? '' : folder + '/';
|
|
40
40
|
yield { ...subNode, name: folder + (0, vfs_1.getNodeName)(subNode) }; // reflect relative path in archive, otherwise way may have name-clashes
|
|
41
41
|
}
|