hfs 3.4.0-alpha1 → 3.4.0-alpha2
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/AccountsPage-CoiPofEv.js +4 -0
- package/admin/assets/{ConfigFilePage-D5TZZyMR.js → ConfigFilePage-jUH2feqi.js} +1 -1
- package/admin/assets/{CustomHtmlPage-DV2-ufWZ.js → CustomHtmlPage-Pl_AJZrl.js} +1 -1
- package/admin/assets/{InternetPage-DT3wSQYQ.js → InternetPage-DIcAwhFk.js} +1 -1
- package/admin/assets/{LangPage-DbXCv7wR.js → LangPage-BwVY8QRD.js} +1 -1
- package/admin/assets/{LogsPage-DmAxUZpu.js → LogsPage-CcjS12cP.js} +1 -1
- package/admin/assets/{MonitorPage-D7ydOeq_.js → MonitorPage-DMVFXyMe.js} +1 -1
- package/admin/assets/OptionsPage-BB40ax9G.js +3 -0
- package/admin/assets/{PluginsPage-BYzfwuvg.js → PluginsPage-CC2dINcb.js} +1 -1
- package/admin/assets/{icons-CfBG9vvB.js → icons-C_ux1zLA.js} +1 -1
- package/admin/assets/{index-CVCnYMoB.js → index-BSSQOMG_.js} +4 -4
- package/admin/assets/{routing-C4l8rnk_.js → routing-Be72yFmd.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{dist-legacy-CKczq_fx.js → dist-legacy-D7Dg5w3L.js} +1 -1
- package/frontend/assets/index-legacy-CnazC3Dj.js +1 -0
- package/frontend/assets/{lib-legacy-D2tyILlj.js → lib-legacy-BxFHpdER.js} +1 -1
- package/frontend/assets/{sha512-legacy-CFcDeiiP.js → sha512-legacy-C8xoQBnP.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +66 -66
- package/package.json +1 -1
- package/plugins/antibrute/plugin.js +72 -18
- package/src/admin-langs/embedded.js +2 -2
- package/src/admin-langs/hfs-admin-lang-ar.json +34 -26
- package/src/admin-langs/hfs-admin-lang-bg.json +35 -27
- package/src/admin-langs/hfs-admin-lang-de.json +37 -29
- package/src/admin-langs/hfs-admin-lang-el.json +38 -30
- package/src/admin-langs/hfs-admin-lang-en.json +8 -0
- package/src/admin-langs/hfs-admin-lang-es.json +37 -29
- package/src/admin-langs/hfs-admin-lang-fi.json +100 -92
- package/src/admin-langs/hfs-admin-lang-fr.json +39 -31
- package/src/admin-langs/hfs-admin-lang-hu.json +37 -29
- package/src/admin-langs/hfs-admin-lang-it.json +35 -27
- package/src/admin-langs/hfs-admin-lang-ja.json +36 -28
- package/src/admin-langs/hfs-admin-lang-ko.json +39 -31
- package/src/admin-langs/hfs-admin-lang-lt.json +35 -27
- package/src/admin-langs/hfs-admin-lang-ms.json +36 -28
- package/src/admin-langs/hfs-admin-lang-nl.json +36 -28
- package/src/admin-langs/hfs-admin-lang-pl.json +36 -28
- package/src/admin-langs/hfs-admin-lang-pt-br.json +36 -28
- package/src/admin-langs/hfs-admin-lang-ro.json +37 -29
- package/src/admin-langs/hfs-admin-lang-ru.json +35 -27
- package/src/admin-langs/hfs-admin-lang-sr-latn.json +35 -27
- package/src/admin-langs/hfs-admin-lang-sr.json +39 -31
- package/src/admin-langs/hfs-admin-lang-th.json +35 -27
- package/src/admin-langs/hfs-admin-lang-tr.json +38 -30
- package/src/admin-langs/hfs-admin-lang-uk.json +35 -27
- package/src/admin-langs/hfs-admin-lang-vi.json +35 -27
- package/src/admin-langs/hfs-admin-lang-zh-hant.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-zh.json +728 -720
- package/src/adminApis.js +2 -1
- package/src/adminLang.js +8 -7
- package/src/api.accounts.js +9 -4
- package/src/api.auth.js +11 -3
- package/src/api.vfs.js +3 -2
- package/src/auth.js +25 -14
- package/src/consoleLog.js +4 -0
- package/src/const.js +1 -1
- package/src/cross-const.js +1 -1
- package/src/cross.js +1 -1
- package/src/customHtml.js +3 -2
- package/src/deletePermission.js +120 -0
- package/src/events.js +6 -0
- package/src/index.js +3 -2
- package/src/lang.js +6 -1
- package/src/langs/embedded.js +2 -2
- package/src/langs/hfs-lang-ar.json +2 -1
- package/src/langs/hfs-lang-bg.json +2 -1
- package/src/langs/hfs-lang-de.json +2 -1
- package/src/langs/hfs-lang-el.json +2 -1
- package/src/langs/hfs-lang-en.json +2 -1
- package/src/langs/hfs-lang-es.json +2 -1
- package/src/langs/hfs-lang-fi.json +2 -1
- package/src/langs/hfs-lang-fr.json +2 -1
- package/src/langs/hfs-lang-hu.json +2 -1
- package/src/langs/hfs-lang-it.json +2 -1
- package/src/langs/hfs-lang-ja.json +2 -1
- package/src/langs/hfs-lang-ko.json +2 -1
- package/src/langs/hfs-lang-lt.json +2 -1
- package/src/langs/hfs-lang-ms.json +2 -1
- package/src/langs/hfs-lang-nl.json +2 -1
- package/src/langs/hfs-lang-pl.json +2 -1
- package/src/langs/hfs-lang-pt-br.json +2 -1
- package/src/langs/hfs-lang-ro.json +2 -1
- package/src/langs/hfs-lang-ru.json +2 -1
- package/src/langs/hfs-lang-sr-latn.json +2 -1
- package/src/langs/hfs-lang-sr.json +2 -1
- package/src/langs/hfs-lang-th.json +2 -1
- package/src/langs/hfs-lang-tr.json +2 -1
- package/src/langs/hfs-lang-uk.json +2 -1
- package/src/langs/hfs-lang-vi.json +2 -1
- package/src/langs/hfs-lang-zh-hant.json +189 -0
- package/src/langs/hfs-lang-zh.json +96 -98
- package/src/listen.js +8 -4
- package/src/middlewares.js +31 -28
- package/src/perm.js +26 -4
- package/src/plugins.js +2 -2
- package/src/serveFile.js +5 -9
- package/src/serveGuiAndSharedFiles.js +9 -8
- package/src/serveGuiFiles.js +2 -2
- package/src/sessionStamp.js +53 -0
- package/src/upload.js +1 -1
- package/src/uploadOwners.js +4 -2
- package/src/vfs.js +80 -33
- package/src/webdav.js +2 -2
- package/admin/assets/AccountsPage-C6J2hRcY.js +0 -4
- package/admin/assets/OptionsPage-DpN3ajRm.js +0 -3
- package/frontend/assets/index-legacy-ZYae30AY.js +0 -1
- package/src/admin-langs/hfs-admin-lang-zh-tw.json +0 -1141
- package/src/langs/hfs-lang-zh-tw.json +0 -187
package/src/serveGuiFiles.js
CHANGED
|
@@ -41,10 +41,10 @@ function serveStatic(uri) {
|
|
|
41
41
|
ctx.state.dontLog = true;
|
|
42
42
|
if (ctx.method === 'OPTIONS') {
|
|
43
43
|
ctx.status = const_1.HTTP_NO_CONTENT;
|
|
44
|
-
ctx.set({ Allow: 'OPTIONS, GET' });
|
|
44
|
+
ctx.set({ Allow: 'OPTIONS, GET, HEAD' });
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
if (ctx.
|
|
47
|
+
if (!ctx.state.getOrHead)
|
|
48
48
|
return ctx.status = const_1.HTTP_METHOD_NOT_ALLOWED;
|
|
49
49
|
const serveApp = shouldServeApp(ctx);
|
|
50
50
|
const fullPath = (0, path_1.join)(root, serveApp ? '/index.html' : ctx.path);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sessionStampsReady = void 0;
|
|
4
|
+
exports.getSessionStamp = getSessionStamp;
|
|
5
|
+
exports.renewSessionStamp = renewSessionStamp;
|
|
6
|
+
exports.syncSessionStamps = syncSessionStamps;
|
|
7
|
+
const persistence_1 = require("./persistence");
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const stamps = new Map();
|
|
10
|
+
const prefix = 'sessionStamp:';
|
|
11
|
+
// random signing keys already invalidate all cookies on restart
|
|
12
|
+
const persistent = process.env.COOKIE_SIGN_KEYS !== undefined;
|
|
13
|
+
exports.sessionStampsReady = load();
|
|
14
|
+
function getSessionStamp(username) {
|
|
15
|
+
return stamps.get(username)?.stamp;
|
|
16
|
+
}
|
|
17
|
+
function renewSessionStamp(username) {
|
|
18
|
+
const current = stamps.get(username);
|
|
19
|
+
if (current)
|
|
20
|
+
setStamp(username, current.credentials);
|
|
21
|
+
}
|
|
22
|
+
function syncSessionStamps(accounts) {
|
|
23
|
+
for (const username of stamps.keys())
|
|
24
|
+
if (!(username in accounts)) {
|
|
25
|
+
stamps.delete(username);
|
|
26
|
+
if (persistent)
|
|
27
|
+
void persistence_1.storedMap.del(prefix + username);
|
|
28
|
+
}
|
|
29
|
+
for (const [username, account] of Object.entries(accounts)) {
|
|
30
|
+
// compare values across reloads and restarts without duplicating credentials in data.kv
|
|
31
|
+
const credentials = (0, node_crypto_1.createHash)('sha256').update(JSON.stringify([
|
|
32
|
+
account?.srp, account?.password, account?.plugin,
|
|
33
|
+
])).digest('hex');
|
|
34
|
+
if (stamps.get(username)?.credentials !== credentials)
|
|
35
|
+
setStamp(username, credentials);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function setStamp(username, credentials) {
|
|
39
|
+
const stamp = `${Date.now()}.${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`;
|
|
40
|
+
const record = { stamp, credentials };
|
|
41
|
+
stamps.set(username, record);
|
|
42
|
+
// accepted tradeoff: with fixed signing keys, a crash before this delayed write may lose a revocation
|
|
43
|
+
if (persistent)
|
|
44
|
+
void persistence_1.storedMap.put(prefix + username, record);
|
|
45
|
+
}
|
|
46
|
+
async function load() {
|
|
47
|
+
for await (const [key, record] of persistence_1.storedMap.iterator({ startsWith: prefix })) {
|
|
48
|
+
if (persistent)
|
|
49
|
+
stamps.set(key.slice(prefix.length), record);
|
|
50
|
+
else // re-enabling the same signing keys later must not revive cookies revoked during this run
|
|
51
|
+
void persistence_1.storedMap.del(key);
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/upload.js
CHANGED
|
@@ -72,7 +72,7 @@ function fillUploadMeta(path, ctx) {
|
|
|
72
72
|
return saveUploadMeta(path, {
|
|
73
73
|
username: (0, auth_1.getCurrentUsername)(ctx) || undefined,
|
|
74
74
|
ip: ctx.ip,
|
|
75
|
-
approved: (0, adminApis_1.ctxAdminAccess)(ctx)
|
|
75
|
+
approved: Boolean((0, adminApis_1.ctxAdminAccess)(ctx)),
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
function getUploadTempFor(fullPath) {
|
package/src/uploadOwners.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.uploadOwners = exports.ownUploadDeleteHours = void 0;
|
|
7
|
+
exports.grantUploadOwnerDelete = grantUploadOwnerDelete;
|
|
7
8
|
exports.isUnfinishedUploadOwner = isUnfinishedUploadOwner;
|
|
8
9
|
exports.setUploadOwner = setUploadOwner;
|
|
9
10
|
exports.moveUploadOwner = moveUploadOwner;
|
|
@@ -33,7 +34,8 @@ setInterval(() => {
|
|
|
33
34
|
void exports.uploadOwners.del(k);
|
|
34
35
|
}
|
|
35
36
|
}, misc_1.MINUTE);
|
|
36
|
-
events_1.default.on('checkVfsPermission',
|
|
37
|
+
events_1.default.on('checkVfsPermission', grantUploadOwnerDelete);
|
|
38
|
+
function grantUploadOwnerDelete({ node, perm, ctx }) {
|
|
37
39
|
if (perm !== 'can_delete' || !node.source)
|
|
38
40
|
return;
|
|
39
41
|
const { vfsPath } = node;
|
|
@@ -49,7 +51,7 @@ events_1.default.on('checkVfsPermission', ({ node, perm, ctx }) => {
|
|
|
49
51
|
}
|
|
50
52
|
if (matchesOwner(owner, ctx))
|
|
51
53
|
return 0;
|
|
52
|
-
}
|
|
54
|
+
}
|
|
53
55
|
function matchesOwner(owner, ctx) {
|
|
54
56
|
const username = (0, auth_1.getCurrentUsername)(ctx);
|
|
55
57
|
const { sessionId } = owner;
|
package/src/vfs.js
CHANGED
|
@@ -27,6 +27,7 @@ exports.simpleWhoToError = simpleWhoToError;
|
|
|
27
27
|
exports.walkNode = walkNode;
|
|
28
28
|
exports.masksCouldGivePermission = masksCouldGivePermission;
|
|
29
29
|
exports.parentMaskApplier = parentMaskApplier;
|
|
30
|
+
exports.renameUnderPath = renameUnderPath;
|
|
30
31
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
31
32
|
const path_1 = require("path");
|
|
32
33
|
const misc_1 = require("./misc");
|
|
@@ -81,6 +82,7 @@ function inheritFromParent(child) {
|
|
|
81
82
|
child.accept ??= parent.accept;
|
|
82
83
|
if (parent.default)
|
|
83
84
|
child.default ??= parent.default;
|
|
85
|
+
child.gui_asset ??= parent.gui_asset;
|
|
84
86
|
return child;
|
|
85
87
|
}
|
|
86
88
|
function isSameFilenameAs(name) {
|
|
@@ -94,6 +96,11 @@ async function isSameFilePath(a, b) {
|
|
|
94
96
|
if (normalizeFilename((0, path_1.resolve)(a)) === normalizeFilename((0, path_1.resolve)(b)))
|
|
95
97
|
return true;
|
|
96
98
|
try {
|
|
99
|
+
if (const_1.IS_WINDOWS) { // pkg's realpath preserves 8.3 names; expanding them here also preserves junction identity
|
|
100
|
+
const [longA, longB] = await Promise.all([convertWindowsPath(a, true), convertWindowsPath(b, true)]);
|
|
101
|
+
if (longA && longB && normalizeFilename((0, path_1.resolve)(longA)) === normalizeFilename((0, path_1.resolve)(longB)))
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
97
104
|
const stats = await Promise.all([promises_1.default.lstat(a), promises_1.default.lstat(b)]);
|
|
98
105
|
if (stats.some(x => x.isSymbolicLink()))
|
|
99
106
|
return false;
|
|
@@ -194,6 +201,9 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
194
201
|
const sourceName = Object.entries(parent.rename || {}).find(([, to]) => sameName(to))?.[0] || name;
|
|
195
202
|
if (!child) {
|
|
196
203
|
child = findSourceChild();
|
|
204
|
+
// a shared alias may publish comment storage only through its explicit VFS name
|
|
205
|
+
if (child?.source && isCommentsFileName((0, path_1.basename)(child.source)))
|
|
206
|
+
return;
|
|
197
207
|
if (!child && const_1.IS_WINDOWS && parent.source) {
|
|
198
208
|
const aliases = await windowsPathNames((0, path_1.join)(parent.source, sourceName), parent.source);
|
|
199
209
|
if (!aliases)
|
|
@@ -248,6 +258,8 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
248
258
|
ret.source = (0, path_1.join)(parent.source, onDisk);
|
|
249
259
|
ret.original = undefined; // this will overwrite the 'original' set in applyParentToChild, so we know this is not part of the vfs
|
|
250
260
|
await setIsFolder(ret);
|
|
261
|
+
if (ret.isFolder === false && isCommentsFileName(onDisk))
|
|
262
|
+
return; // implicit comment storage contains metadata for files the requester may not be allowed to see
|
|
251
263
|
if (assumeMissingToBeFolder)
|
|
252
264
|
ret.isFolder ??= true;
|
|
253
265
|
return ret;
|
|
@@ -269,6 +281,9 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
269
281
|
return parent.children?.find(x => getSourceNames(x, parent).some(source => normalized.has(normalizeFilename(source))));
|
|
270
282
|
}
|
|
271
283
|
}
|
|
284
|
+
function isCommentsFileName(name) {
|
|
285
|
+
return (0, comments_1.usingDescriptIon)() && [comments_1.DESCRIPT_ION, comments_1.DESCRIPT_ION_ALT].some(isSameFilenameAs(name));
|
|
286
|
+
}
|
|
272
287
|
const smartUncFolderDetection = (0, config_1.defineConfig)(misc_1.CFG.smart_unc_folder_detection, false);
|
|
273
288
|
async function setIsFolder(node) {
|
|
274
289
|
if (!node.source)
|
|
@@ -402,7 +417,7 @@ function nodeIsLink(node) {
|
|
|
402
417
|
function hasPermission(node, perm, ctx) {
|
|
403
418
|
return !statusCodeForMissingPerm(node, perm, ctx, false);
|
|
404
419
|
}
|
|
405
|
-
function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
420
|
+
function statusCodeForMissingPerm(node, perm, ctx, assign = true, checkListeners = true) {
|
|
406
421
|
const ret = getCode();
|
|
407
422
|
if (ret && assign) {
|
|
408
423
|
ctx.status = ret;
|
|
@@ -432,7 +447,7 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
|
432
447
|
} while (1);
|
|
433
448
|
if ((0, misc_1.isWhoObject)(who) || isWhoVfsPerms(who))
|
|
434
449
|
throw Error(`permission type-guard: ${JSON.stringify(who)}`);
|
|
435
|
-
const first = lodash_1.default.max(events_1.default.emit('checkVfsPermission', { who, node, perm, ctx }));
|
|
450
|
+
const first = checkListeners ? lodash_1.default.max(events_1.default.emit('checkVfsPermission', { who, node, perm, ctx })) : undefined;
|
|
436
451
|
if (first !== undefined)
|
|
437
452
|
return first;
|
|
438
453
|
return simpleWhoToError(who, ctx)
|
|
@@ -462,7 +477,6 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
462
477
|
const { source } = parent;
|
|
463
478
|
const taken = new Set();
|
|
464
479
|
const takenSources = new Set();
|
|
465
|
-
const maskApplier = parentMaskApplier(parent);
|
|
466
480
|
const visitLater = [];
|
|
467
481
|
const childrenWorking = parent.children?.length && Promise.all(parent.children.map(async (child) => {
|
|
468
482
|
if (ctx?.isAborted())
|
|
@@ -473,7 +487,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
473
487
|
for (const sourceName of getSourceNames(child, parent))
|
|
474
488
|
takenSources.add(normalizeFilename(prefixPath + sourceName));
|
|
475
489
|
const item = setVfsPath({ ...child, original: child, name, parent }, name, parent);
|
|
476
|
-
if (await cantSee(item))
|
|
490
|
+
if (await cantSee(item, parent, nodeName))
|
|
477
491
|
return;
|
|
478
492
|
if (item.source && !item.children?.length && !item.see_without_probing) // real items must be accessible, unless probing was explicitly disabled
|
|
479
493
|
try {
|
|
@@ -483,11 +497,10 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
483
497
|
return;
|
|
484
498
|
}
|
|
485
499
|
const isFolder = nodeIsFolder(child);
|
|
486
|
-
if (
|
|
500
|
+
if (canEmit(item, isFolder))
|
|
487
501
|
stream.push(item);
|
|
488
502
|
if (!depth || !isFolder || cantRecur(item))
|
|
489
503
|
return;
|
|
490
|
-
inheritMasks(item, parent);
|
|
491
504
|
visitLater.push([item, name]); // prioritize siblings
|
|
492
505
|
}));
|
|
493
506
|
try {
|
|
@@ -497,31 +510,52 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
497
510
|
&& !hasPermission(parent, requiredPerm, ctx)
|
|
498
511
|
&& !masksCouldGivePermission(parent.masks, requiredPerm))
|
|
499
512
|
return;
|
|
500
|
-
const
|
|
513
|
+
const diskFolders = new Map();
|
|
514
|
+
diskFolders.set('', { node: parent, virtualPath: '' });
|
|
515
|
+
const applyPathMask = parentMaskApplier(parent, true, true);
|
|
516
|
+
const applyRootNameMask = parentMaskApplier(parent, false, true);
|
|
501
517
|
try {
|
|
502
518
|
await (0, walkDir_1.walkDir)(source, { depth, ctx, hidden: showHiddenFiles.get(), parallelizeRecursion }, async (entry) => {
|
|
503
519
|
if (ctx?.isAborted())
|
|
504
520
|
return null;
|
|
505
|
-
if ((0, comments_1.usingDescriptIon)() && (entry.name === comments_1.DESCRIPT_ION || entry.name === comments_1.DESCRIPT_ION_ALT))
|
|
506
|
-
return;
|
|
507
521
|
const { path } = entry; // this path is not the original deprecated property: we are overwriting/reusing it
|
|
508
522
|
const isFolder = entry.isDirectory();
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
523
|
+
if (!isFolder && isCommentsFileName(entry.name))
|
|
524
|
+
return;
|
|
525
|
+
const parentPath = (0, path_1.dirname)(path) === '.' ? '' : (0, path_1.dirname)(path);
|
|
526
|
+
const evaluatedParent = diskFolders.get(parentPath);
|
|
527
|
+
if (!evaluatedParent)
|
|
528
|
+
return false;
|
|
529
|
+
const physicalName = (0, path_1.basename)(path);
|
|
530
|
+
const virtualName = evaluatedParent.node.rename?.[physicalName] || physicalName;
|
|
531
|
+
const virtualPath = evaluatedParent.virtualPath
|
|
532
|
+
? evaluatedParent.virtualPath + '/' + virtualName
|
|
533
|
+
: virtualName;
|
|
534
|
+
const name = prefixPath + virtualPath;
|
|
516
535
|
if (taken.has(normalizeFilename(name))
|
|
517
536
|
|| takenSources.has(normalizeFilename(prefixPath + path))) // taken by vfs node above
|
|
518
537
|
return false; // false just in case it's a folder
|
|
519
|
-
const item = setVfsPath({
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
538
|
+
const item = setVfsPath({
|
|
539
|
+
name: virtualName,
|
|
540
|
+
isFolder,
|
|
541
|
+
source: (0, path_1.join)(source, path),
|
|
542
|
+
parent: evaluatedParent.node,
|
|
543
|
+
rename: renameUnderPath(evaluatedParent.node.rename, physicalName),
|
|
544
|
+
stats: entry.stats,
|
|
545
|
+
}, virtualName, evaluatedParent.node);
|
|
546
|
+
const rootMaskProperties = { isFolder };
|
|
547
|
+
const exceptions = applyPathMask(rootMaskProperties, virtualPath);
|
|
548
|
+
for (const key of applyRootNameMask(rootMaskProperties, virtualName))
|
|
549
|
+
exceptions.add(key);
|
|
550
|
+
for (const key of exceptions)
|
|
551
|
+
if (!(key in rootMaskProperties))
|
|
552
|
+
rootMaskProperties[key] = parent[key];
|
|
553
|
+
if (await cantSee(item, evaluatedParent.node, virtualName, rootMaskProperties)) // can't see: don't produce and don't recur
|
|
523
554
|
return false;
|
|
524
|
-
if (
|
|
555
|
+
if (isFolder)
|
|
556
|
+
diskFolders.set(path, { node: item, virtualPath });
|
|
557
|
+
item.name = name;
|
|
558
|
+
if (canEmit(item, isFolder))
|
|
525
559
|
stream.push(item);
|
|
526
560
|
if (cantRecur(item))
|
|
527
561
|
return false;
|
|
@@ -544,10 +578,16 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
544
578
|
function cantRecur(item) {
|
|
545
579
|
return ctx && !hasPermission(item, 'can_list', ctx);
|
|
546
580
|
}
|
|
581
|
+
function canEmit(item, isFolder) {
|
|
582
|
+
return !(requiredPerm && ctx && !hasPermission(item, requiredPerm, ctx))
|
|
583
|
+
&& (onlyFiles ? !isFolder : (!onlyFolders || isFolder));
|
|
584
|
+
}
|
|
547
585
|
// item will be changed, so be sure to pass a temp node
|
|
548
|
-
async function cantSee(item) {
|
|
549
|
-
|
|
586
|
+
async function cantSee(item, itemParent, virtualName, pathPermissions) {
|
|
587
|
+
inheritMasks(item, itemParent, virtualName);
|
|
588
|
+
await parentMaskApplier(itemParent)(item, virtualName);
|
|
550
589
|
inheritFromParent(item);
|
|
590
|
+
Object.assign(item, pathPermissions);
|
|
551
591
|
if (ctx && !hasPermission(item, 'can_see', ctx))
|
|
552
592
|
return true;
|
|
553
593
|
item.isTemp = true;
|
|
@@ -564,7 +604,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
564
604
|
function masksCouldGivePermission(masks, perm) {
|
|
565
605
|
return masks !== undefined && Object.values(masks).some(props => props[perm] || masksCouldGivePermission(props.masks, perm));
|
|
566
606
|
}
|
|
567
|
-
function parentMaskApplier(parent, pathBased = false) {
|
|
607
|
+
function parentMaskApplier(parent, pathBased = false, trackExceptions = false) {
|
|
568
608
|
// rules are met in the parent.masks object from nearest to farthest, but since we finally apply with _.defaults, the nearest has precedence in the final result
|
|
569
609
|
const matchers = (0, misc_1.onlyTruthy)(lodash_1.default.map(parent.masks, (mods, mask) => {
|
|
570
610
|
if (!mods)
|
|
@@ -582,11 +622,6 @@ function parentMaskApplier(parent, pathBased = false) {
|
|
|
582
622
|
if (pathBased) {
|
|
583
623
|
if (!mask.includes('/'))
|
|
584
624
|
return;
|
|
585
|
-
// avoid evaluating twice masks like **/*.png because parentMaskApplier already handles them by basename
|
|
586
|
-
const m = /^(!?)\*\*\//.exec(mask);
|
|
587
|
-
// this keeps the fast basename path as source-of-truth for patterns that collapse to a filename after **/
|
|
588
|
-
if (m && !mask.slice(m[0].length).includes('/'))
|
|
589
|
-
return;
|
|
590
625
|
}
|
|
591
626
|
else {
|
|
592
627
|
const m = /^(!?)\*\*\//.exec(mask); // ** globstar matches also zero subfolders, so this mask must be applied here too
|
|
@@ -594,22 +629,34 @@ function parentMaskApplier(parent, pathBased = false) {
|
|
|
594
629
|
if (!mask)
|
|
595
630
|
return;
|
|
596
631
|
}
|
|
597
|
-
return mask && {
|
|
632
|
+
return mask && {
|
|
633
|
+
matcher: (0, misc_1.makeMatcher)(mask),
|
|
634
|
+
exceptionMatcher: trackExceptions && mask.startsWith('!') ? (0, misc_1.makeMatcher)(mask.slice(1)) : undefined,
|
|
635
|
+
mods,
|
|
636
|
+
mustBeFolder,
|
|
637
|
+
};
|
|
598
638
|
}));
|
|
599
639
|
return (item, virtualName = (pathBased ? lodash_1.default.identity : path_1.basename)(getNodeName(item))) => {
|
|
600
640
|
// depth traversal passes full relative paths, while node traversal still matches only basenames
|
|
641
|
+
const exceptions = new Set();
|
|
601
642
|
let isFolder = undefined;
|
|
602
|
-
for (const { matcher, mods, mustBeFolder } of matchers) {
|
|
643
|
+
for (const { matcher, exceptionMatcher, mods, mustBeFolder } of matchers) {
|
|
603
644
|
if (mustBeFolder !== undefined) {
|
|
604
645
|
isFolder ??= nodeIsFolder(item);
|
|
605
646
|
if (mustBeFolder !== isFolder)
|
|
606
647
|
continue;
|
|
607
648
|
}
|
|
608
|
-
if (!matcher(virtualName))
|
|
649
|
+
if (!matcher(virtualName)) {
|
|
650
|
+
if (exceptionMatcher?.(virtualName))
|
|
651
|
+
for (const key of misc_1.PERM_KEYS)
|
|
652
|
+
if (mods[key] !== undefined && !(key in item))
|
|
653
|
+
exceptions.add(key);
|
|
609
654
|
continue;
|
|
655
|
+
}
|
|
610
656
|
item.masks &&= lodash_1.default.merge(lodash_1.default.cloneDeep(mods.masks), item.masks); // item.masks must take precedence
|
|
611
657
|
lodash_1.default.defaults(item, mods);
|
|
612
658
|
}
|
|
659
|
+
return exceptions;
|
|
613
660
|
};
|
|
614
661
|
}
|
|
615
662
|
// propagates masks, don't apply
|
|
@@ -619,7 +666,7 @@ function inheritMasks(item, parent, virtualBasename = getNodeName(item)) {
|
|
|
619
666
|
return;
|
|
620
667
|
const o = {};
|
|
621
668
|
for (const [k, v] of Object.entries(masks)) {
|
|
622
|
-
if (k
|
|
669
|
+
if (/^!?\*\*/.test(k)) {
|
|
623
670
|
o[k] = v;
|
|
624
671
|
continue;
|
|
625
672
|
}
|
package/src/webdav.js
CHANGED
|
@@ -133,7 +133,7 @@ const webdav = async (ctx, next) => {
|
|
|
133
133
|
const isCorsPreflight = ctx.method === 'OPTIONS' && ctx.get('Access-Control-Request-Method');
|
|
134
134
|
const isKnownWebdavAgent = KNOWN_UA.test(ua) || webdavDetectedAgents.has(webdavAgentKey(ctx, ua));
|
|
135
135
|
const isWebdavAuthRequest = !isCorsPreflight && (ctx.method === 'OPTIONS' || WEBDAV_METHODS.has(ctx.method) || WEBDAV_HINT_HEADERS.some(h => ctx.get(h))
|
|
136
|
-
|| ctx.
|
|
136
|
+
|| ctx.state.getOrHead && isKnownWebdavAgent);
|
|
137
137
|
if (isWebdavAuthRequest)
|
|
138
138
|
ctx.state.webdavDetected = true;
|
|
139
139
|
if (isWebdavAuthRequest && ua && (0, auth_1.getCurrentUsername)(ctx))
|
|
@@ -147,7 +147,7 @@ const webdav = async (ctx, next) => {
|
|
|
147
147
|
return;
|
|
148
148
|
if (ctx.method === 'OPTIONS')
|
|
149
149
|
return handleOptions();
|
|
150
|
-
if (ctx.
|
|
150
|
+
if (ctx.state.getOrHead && isWebdavAuthRequest)
|
|
151
151
|
return handleGet();
|
|
152
152
|
switch (ctx.method) {
|
|
153
153
|
case 'PUT': return handlePut();
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{B as t,Bt as n,C as r,Dt as i,E as a,En as o,Et as s,F as c,G as l,J as u,Kt as d,L as f,Nt as p,O as m,Ot as h,Qt as g,Rt as _,St as v,U as y,Ut as b,V as x,X as ee,Y as S,Yt as C,Z as w,Zt as T,_ as E,_r as D,_t as O,an as k,ar as A,at as te,b as ne,bn as j,bt as M,cn as N,d as re,dt as P,et as ie,fr as F,ft as I,gn as L,gr as R,h as z,hr as B,ht as ae,ir as oe,it as V,jn as se,k as ce,kn as le,lr as H,lt as ue,mn as de,ot as fe,p as pe,qt as me,ur as U,v as he,vr as W,vt as G,xt as ge,y as _e,yr as ve,z as K,zt as ye}from"./index-CVCnYMoB.js";import{_ as be,g as xe,h as Se,m as Ce,v as we}from"./icons-CfBG9vvB.js";var q=e(ve());U();var Te=[`password`,`password2`,`adminActualAccess`,`hasPassword`,`invalidated`,`canLogin`,`members`];function Ee({account:e,done:n,groups:c,addToBar:l,reload:u}){var d,p;let{username:m}=I(),[h,_]=(0,q.useState)(e),[b,x]=(0,q.useState)([]),ee=le();(0,q.useEffect)(()=>{var t;_(e),x(c.filter(t=>t!==e.username)),ee||(t=E.current)==null||(t=t.querySelector(`input`))==null||t.focus()},[JSON.stringify(e)]);let w=!e.username,{isGroup:T}=h,E=(0,q.useRef)(),{members:D}=e,A=(d=e.plugin)==null?void 0:d.auth;return(0,q.createElement)(O,{key:e.username,formRef:E,values:h,set(e,t){_(n=>H(H({},n),{},{[t]:e}))},barSx:H({gap:2,width:`100%`},ue()),stickyBar:!0,addToBar:[!w&&(0,q.createElement)(t,H(H({icon:a,title:k`Delete`,confirm:k(`Delete {username}?`,{username:e.username})},m===e.username&&{disabled:!0,title:k`Cannot delete current account`}),{},{onClick:()=>F(`del_account`,{username:e.username}).then(u)})),(0,q.createElement)(t,{icon:Ce,title:k`Invalidate past sessions`+(e.invalidated?`
|
|
2
|
-
`+k(`(already invalidated sessions before {timestamp})`,{timestamp:s(e.invalidated)}):``),confirm:k(`Invalidate all sessions up to now for "{username}"?`,{username:e.username}),doneMessage:!0,onClick:()=>F(`invalidate_sessions`,{username:e.username}).then(u)}),...oe(l)],fields:[{k:`username`,label:k(T?`Group name`:`Username`),autoComplete:`off`,required:!0,md:T&&!A?12:4,helperText:k`Case-insensitive`,getError:t=>t!==e.username&&F(`get_account`,{username:t}).then(t=>(t==null?void 0:t.username)===e.username?k`usernames are case-insensitive`:k`already used`,()=>!1)},A&&{k:``,md:8,comp:(0,q.createElement)(g,{severity:`info`},k`Authentication handled by a plugin`)},!T&&!A&&{k:`password`,xs:6,md:4,type:`password`,autoComplete:`new-password`,required:w,label:w?k`Password`:k`Change password`},!T&&!A&&{k:`password2`,xs:6,md:4,type:`password`,autoComplete:`new-password`,label:k`Repeat password`,getError:(e,{values:t})=>(e||``)!==(t.password||``)&&k`Enter same password`},{k:`disabled`,comp:G,fromField:e=>!e,toField:e=>!e,label:k`Enabled`,xs:12,sm:6,lg:4,helperText:h.disabled||h.canLogin!==!1?k`login_disabled_by_account_or_groups`:(0,q.createElement)(C,{sx:{color:`warning.main`},component:`span`},new Date(e.expire)<new Date?k`Login is prevented because account is expired`:k`login_disabled_by_groups`)},{k:`ignore_limits`,label:k`Ignore limits`,comp:G,xs:12,sm:6,lg:4,helperText:h.ignore_limits?k`Speed limits don't apply to this account`:k`Speed limits apply to this account`},H({k:`admin`,comp:G,fromField:e=>e||null,label:k`Admin-panel access`,xs:12,sm:6,lg:4,helperText:k`To access THIS interface you are using right now`},!e.admin&&e.adminActualAccess&&{value:!0,disabled:!0,helperText:k`inherited_permission_notice`}),!T&&{k:`require_password_change`,label:k`Require password change`,comp:G,xs:12,sm:6,lg:4,helperText:k`At next login, but can be dismissed`},{k:`disable_password_change`,label:k`Password change`,comp:v,xs:12,sm:6,lg:4,defaultValue:null,options:{[k(`default_setting`,{defaultValue:k(h.canChangePassword?`Allowed`:`Disabled`)})]:null,[k`Allowed`]:!1,[k`Disabled`]:!0}},D?T&&!D.length?(0,q.createElement)(C,{},k`No members`):D.length>0&&(0,q.createElement)(K,{gap:0,flexWrap:`wrap`},k(`members_count`,{n:D.length}),o(`,\xA0`,(p=e.members)==null?void 0:p.map(e=>(0,q.createElement)(c.includes(e)?`i`:`span`,{},e))),(0,q.createElement)(f,{icon:a,confirm:k(`delete_items`,{n:e.members.length}),onClick:()=>F(`del_account`,{username:e.members}).then(u),sx:{verticalAlign:`text-top`}})):null,T&&(0,q.createElement)(g,{severity:`info`},k`group_add_members_hint`),{k:`belongs`,comp:ge,label:k`Inherit from groups`,options:b,sm:6,lg:4,helperText:[k`Specify groups to inherit permissions from.`,T&&k`A group can inherit from another group.`,!b.length&&k`no_groups_available`].filter(Boolean).join(` `)},{k:`allow_net`,comp:y,label:k`Allowed network address`,sm:6,lg:4,placeholder:k`any address`},!T&&{k:`auto_login_net`,comp:y,label:k`Auto-login by IP address`,sm:6,lg:4,placeholder:k`none`},{k:`redirect`,label:k`Redirect`,comp:re,placeholder:k`no`,sm:6,lg:4,helperText:k`account_redirect_hint`},{k:`expire`,label:k`Expiration`,sm:6,lg:4,comp:r,toField:e=>e&&new Date(e),helperText:k`When expired, login won't be allowed`},H(H({k:`days_to_live`,label:k`Days to live`,sm:6,lg:4,comp:M,step:`any`,min:1/1e3},h.expire&&{xs:12,disabled:!0,sx:{opacity:.2}}),{},{helperText:k`Used to set expiration on first login`}),{k:`notes`,label:k`Notes`,multiline:!0,sm:6,lg:4}],onError:V,save:H(H({children:k`Save`},S(i(h,e))),{},{onClick(){return W(function*(){var t;let{password:r=``,password2:i,adminActualAccess:a,hasPassword:o,invalidated:s,canLogin:c,members:l}=h,u=se(h,Te),d=((t=E.current)==null?void 0:t.querySelector(`button.saveBtn`))||void 0;if(w){let e=yield F(`add_account`,u);if(r)try{yield N(h.username,r)}catch(e){throw F(`del_account`,{username:h.username}),e}n(e==null?void 0:e.username,d);return}let f=yield F(`set_account`,{username:e.username,changes:u});r&&(yield N(h.username,r),_(e=>H(H({},e),{},{password:``,password2:``}))),e.username===m&&(P.username=f.username),n(f==null?void 0:f.username,d)})()}})})}var J=e(D());R(),U();var Y={fieldSeparator:`,`,lineSeparator:`
|
|
3
|
-
`,quote:`"`},X={field:0,fieldOffset:0,line:0,lineOffset:0,quoted:!1,appendCell:!1,appendField:!1,appendRow:!1},De=class extends Error{constructor(e,t){super(`Invalid CSV at ${e}:${t}`)}},Oe=class{constructor(e=Y){B(this,`_rows`,[]),B(this,`_row`,[]),B(this,`_cell`,``),B(this,`_options`,Y),B(this,`_state`,H({},X)),B(this,`_index`,0),B(this,`_current`,``),B(this,`_previous`,``),B(this,`_quoteState`,H({},X)),this._options=Object.assign({},Y,e)}parse(e){for(this.reset(),this._index=0;this._index<e.length;this._index++)this._state.appendCell=!0,this._previous=this._current,this._current=e[this._index],this.handleNext();if(this._row.length>0&&(this.addField(this.fieldValue(this._cell),this._row,this._state),this.addRow(this._row,this._rows,this._state)),this._state.quoted)throw new De(this._quoteState.line,this._quoteState.lineOffset);return this.makeImmutable(),this._rows}get rows(){return this._rows}get json(){if(this.rows.length>0){let e=this.rows[0].filter(e=>typeof e==`string`);return Object.freeze(this.rows.filter((e,t)=>e&&t>0).map(t=>{let n={};return e.forEach((e,r)=>{n[e]=t[r]}),Object.freeze(n)}))}return Object.freeze([])}handleNext(){this.handleQuote()||this.handleFieldSeparator()||this.handleLineSeparator(),this.processState()}handleQuote(){return this._current===this._options.quote?(this._quoteState=H({},this._state),this._previous===`\\`?this.handleQuoteEscaped():this.handleQuoteNotEscaped(),!0):!1}handleQuoteEscaped(){this._cell=this._cell.slice(0,Math.max(0,this._cell.length-1))}handleQuoteNotEscaped(){if(this._cell.length===0||this._state.quoted)this._state.quoted=!this._state.quoted;else throw new De(this._state.line,this._state.lineOffset);this._state.appendCell=!1}handleFieldSeparator(){return this._current===this._options.fieldSeparator?(this._state.quoted||(this._state.appendCell=!1,this._state.appendField=!0),!0):!1}handleLineSeparator(){return this._current===this._options.lineSeparator?(this._state.quoted||(this._state.appendCell=!1,this._state.appendField=!0,this._state.appendRow=!0),!0):!1}processState(){this._state.appendCell&&(this._cell+=this._current),this._state.appendField&&(this.addField(this.fieldValue(this._cell),this._row,this._state),this._cell=``),this._state.appendRow&&(this.addRow(this._row,this._rows,this._state),this._row=[]),this._state.lineOffset++,this._state.fieldOffset++}fieldValue(e){return e}addField(e,t,n){t.push(e),n.field++,n.fieldOffset=-1,n.appendField=!1}addRow(e,t,n){t.push(e),n.field=0,n.line++,n.lineOffset=-1,n.appendRow=!1}makeImmutable(){this.rows.forEach(e=>{e.forEach(e=>Object.freeze(e)),Object.freeze(e)}),Object.freeze(this._rows)}reset(){this._rows=[],this._row=[],this._cell=``,this._state=H({},X),this._index=0,this._current=``,this._previous=``,this._quoteState=H({},X)}};U();function ke(e){return Z.apply(this,arguments)}function Z(){return Z=W(function*(e){L(function(){var t=W(function*(t){let n=t==null?void 0:t[0];if(!n)return;let r=yield de(n);if(!r)return;let i=new Oe().parse(r.trim()),a={comp:M,min:1,max:9,xs:6,typing:!0},o={skipFirstLines:0,usernameColumn:1,passwordColumn:2,groupColumn:3,redirectColumn:4,overwriteExistingAccounts:!1},s=yield fe({title:k`Import accounts from CSV`,dialogProps:{maxWidth:`sm`},values:o,form:e=>{let t=i[e.skipFirstLines||0]||[],n=l(t,H(H({},o),e));return{save:{startIcon:(0,q.createElement)(z),children:k`Go`},fields:[(0,q.createElement)(C,{sx:{p:1}},k`Total lines:`,i.length),{k:`skipFirstLines`,label:k`Skip First Lines`,comp:M,max:i.length-1,typing:!0,md:6,getError:e=>e!=null&&!Number.isInteger(e)&&`Enter an integer`,helperText:(0,q.createElement)(q.Fragment,{},k`First line: `,(0,q.createElement)(`code`,{},t.join(`, `)))},{k:`overwriteExistingAccounts`,label:k`Overwrite Existing Accounts`,comp:G,md:6},H(H({k:`usernameColumn`,label:k`Username Column`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},k`First username: `,n.u)}),H(H({k:`passwordColumn`,label:k`Password Column`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},k`First password: `,n.p)}),H(H({k:`groupColumn`,label:k`Group Column`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},k`First group: `,n.g)}),H(H({k:`redirectColumn`,label:k`Redirect Column`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},k`First redirect: `,n.r)})]}}});if(!s)return;let{close:c}=j({title:k`Importing...`,Content(){let[t,n]=(0,q.useState)(0),[r,a]=(0,q.useState)();return(0,q.useEffect)(()=>{let t=!1;return setTimeout(W(function*(){if(t)return;let r=0,o=0,u=s.skipFirstLines,d=i.length-u,f=0;try{for(let e of i){var p;if(t)return;if(u){u--;continue}let i=l(e,s);a(i),n(f++/d),yield F(`add_account`,{username:i.u,belongs:(p=i.g)==null?void 0:p.split(`,`),redirect:i.r,overwrite:s.overwriteExistingAccounts}).then(()=>{if(i.p)return N(i.u,i.p)}).catch(e=>{if(e.code===409)return o++;r++})}}finally{c();let t=f-r-o;V([k`Results:`,k(`account_import_failed`,{n:r}),k(`account_import_succeeded`,{n:t}),k(`account_import_skipped_existing`,{n:o})].join(`
|
|
4
|
-
`),!t&&r?`error`:r||o?`warning`:`success`),e==null||e()}})),()=>{t=!0}},[]),(0,q.createElement)(C,{sx:{display:`flex`,gap:2,alignItems:`center`}},(0,q.createElement)(x,{icon:m,progress:t}),r==null?void 0:r.u)}});function l(e,t){return{u:e[t.usernameColumn-1],p:e[t.passwordColumn-1],g:e[t.groupColumn-1],r:e[t.redirectColumn-1]}}});return function(e){return t.apply(this,arguments)}}(),{multiple:!1,accept:`.csv`})}),Z.apply(this,arguments)}U();var Ae=` `,Q=e=>e==null?void 0:e.split(Ae).at(-1);function je(){let{username:e,accountsAsTree:r}=I(),{data:i,reload:o,element:s}=c(`get_accounts`),[m,v]=(0,q.useState)([]),y=Array.isArray(m);(0,q.useEffect)(()=>{Array.isArray(i==null?void 0:i.list)&&y&&v(m.filter(e=>i.list.find(t=>(t==null?void 0:t.username)===Q(e))))},[i]);let x=(0,q.useMemo)(()=>i&&J.default.sortBy(i.list,[e=>!e.isGroup,e=>!e.adminActualAccess,`username`]),[i]),S=y&&J.default.find(x,{username:Q(m[0])}),D=w(`md`),O=!(m.length>0)||!x?null:y&&m.length>1?(0,q.createElement)(q.Fragment,{},(0,q.createElement)(K,{},(0,q.createElement)(T,{variant:`h6`},k(`select_count`,{n:m.length})),(0,q.createElement)(f,{onClick:z,icon:a},k`Remove`)),(0,q.createElement)(n,{},J.default.uniq(m.map(Q)).map(e=>(0,q.createElement)(ye,{key:e},(0,q.createElement)(_,{},e))))):A(S||L(),e=>(0,q.createElement)(Ee,{account:e,groups:x.filter(e=>e.isGroup).map(e=>e.username),addToBar:D&&[(0,q.createElement)(C,{sx:{flex:1}}),$(e,{fontSize:`large`,sx:{p:1}}),(0,q.createElement)(t,{icon:ae,title:k`Close`,onClick:R})],reload:o,done(e,t){v(D?[e]:[]),o(),l(``,t)}}));(0,q.useEffect)(()=>{if(D||!O||!m.length)return;let{close:e}=j({title:J.default.isString(m)?J.default.startCase(m):m.length>1?k`Multiple selection`:S?k(S.isGroup?`Group: `:`User: `)+S.username:`?`,Content:()=>O,onClose(e){e||R()}});return()=>void e(!0)},[D,m,S]);let M={height:`100%`,display:`flex`,flexDirection:`column`,overflow:`auto`},[N,re]=ie(k`Show tree`,k`Show list`,()=>({icon:p}),r);return P.accountsAsTree=N,s||(0,q.createElement)(b,{container:!0,sx:{rowSpacing:1,columnSpacing:2,top:0,flex:`1 1 auto`,height:0}},(0,q.createElement)(b,{size:{xs:12,md:5,lg:4,xl:5},sx:M},(0,q.createElement)(C,{sx:{display:`flex`,flexWrap:`wrap`,gap:2,mb:2,boxShadow:e=>`0px -8px 4px 10px ${e.palette.background.paper}`,position:`sticky`,top:0,zIndex:2,backgroundColor:`background.paper`,width:`fit-content`}},(0,q.createElement)(pe,{variant:`contained`,startIcon:(0,q.createElement)(xe),items:[{children:k`user`,onClick:()=>v(`new-user`)},{children:k`group`,onClick:()=>v(`new-group`)},{children:k`from CSV`,onClick:()=>ke(o)}]},k`Add`),ee(o),re,(x==null?void 0:x.length)>0&&(0,q.createElement)(T,{sx:{p:1}},k(`accounts_count`,{n:x.length}))),!(x!=null&&x.length)&&(0,q.createElement)(g,{severity:`info`},h(k`remote_admin_account_required`)),(0,q.createElement)(ne,{multiSelect:!0,sx:{pr:4,pb:2,minWidth:`15em`},selectedItems:y?m:[],slots:{collapseIcon:E,expandIcon:he},onSelectedItemsChange(e,t){var n,r;!(e==null||(n=e.target)==null||(r=n.closest)==null)&&r.call(n,`.MuiTreeItem-iconContainer`)||v(t)}},x&&(function e(t,n=``){return t.map(t=>{var r,i;return(0,q.createElement)(_e,{key:t.username,itemId:n+t.username,label:(0,q.createElement)(C,{sx:{display:`flex`,flexWrap:`wrap`,padding:`.2em 0`,columnGap:`.5em`,alignItems:`center`}},$(t),(t.disabled||t.canLogin===!1)&&u(we,t.disabled?k`Disabled`:k`Disabled by its groups`,t.disabled?void 0:{color:`text.secondary`}),(t.expire||t.days_to_live)&&(0,q.createElement)(Se),t.adminActualAccess&&u(ce,k`Can login into Admin`),t.username,!!((r=t.belongs)!=null&&r.length)&&(0,q.createElement)(C,{sx:{color:`text.secondary`,fontSize:`small`}},`(`,(i=t.belongs)==null?void 0:i.join(`, `),`)`))},N&&e(x.filter(e=>{var n;return(n=t.directMembers)==null?void 0:n.includes(e.username)}),n+t.username+Ae))})})(N?x.filter(e=>!x.some(t=>{var n;return(n=t.members)==null?void 0:n.includes(e.username)})):x))),D&&O&&(0,q.createElement)(b,{size:`grow`,sx:H(H({},M),{},{maxWidth:`100%`})},(0,q.createElement)(me,{sx:{overflow:`initial`}},(0,q.createElement)(d,{},O))));function L(){return{username:``,hasPassword:m===`new-user`,adminActualAccess:!1,invalidated:void 0,canLogin:!0,canChangePassword:!0,isGroup:m===`new-group`,members:[],directMembers:[]}}function R(){v([])}function z(){return B.apply(this,arguments)}function B(){return B=W(function*(){if(typeof m==`string`||m.some(t=>Q(t)===e)&&!(yield te(k`current_account_delete_warning`)))return;let t=J.default.without(J.default.uniq(m.map(Q)),e);if(!t.length)return V(k`Nothing to delete`,`info`);if(!(yield te(k(`delete_items`,{n:t.length}))))return;let n=[];for(let e of t)(yield F(`del_account`,{username:e}).then(()=>1,()=>0))||n.push(e);if(o(),n.length)return V(k(`items_delete_failed`,{n:n.length,list:n.join(`, `)}),`error`)}),B.apply(this,arguments)}}function $(e,t={}){return(0,q.createElement)(e.isGroup?m:be,t)}export{je as default};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{Cn as t,Ct as n,F as r,Fn as i,Ft as a,G as o,H as s,J as c,Jt as l,M as u,N as d,Nn as f,On as p,Ot as m,Pn as h,Pt as g,Qn as _,S as v,Sn as y,St as b,Tt as ee,U as x,Vn as te,W as S,Wt as C,Y as ne,Yt as w,Z as re,_n as ie,_r as ae,_t as oe,a as T,an as E,ar as D,at as O,bt as k,ct as se,dt as A,fr as ce,ft as le,it as j,j as ue,l as de,lr as M,nn as fe,or as N,pt as pe,s as me,sn as he,t as ge,tr as _e,tt as P,u as F,ur as I,vr as ve,vt as L,xn as ye,yr as R}from"./index-CVCnYMoB.js";import{t as be}from"./icons-CfBG9vvB.js";import{t as xe}from"./routing-C4l8rnk_.js";var z=e(R()),B=e(ae()),V=`//---`;function Se(e){let t=e.split(RegExp(`(?:^|
|
|
2
|
-
)`+B.default.escapeRegExp(V),`g`)),n=[],r=!0;for(let e of t)if(r){r=!1;let t=e.trim();t&&n.push({name:``,code:t})}else{let[t,r]=_(`
|
|
3
|
-
`,e);n.push({name:t,code:r})}return n}var H=F();I();var U,Ce=[`net`,`frontend`,`uploads`,`others`],we=y({changes:{}});t(A,e=>{e.some(e=>e[1][0]===`config`)&&Te()});function W(){var e,t,n;let{data:_,reload:y,element:C}=r(`get_config`,{omit:[`vfs`]}),ae=le(),{changes:F}=ye(we),I=r(_&&`get_status`),R=I.data,W=I.reload,ke=()=>d(e=>{U&&Object.assign(U,e),setTimeout(W,1e3),setTimeout(W,2e3)});(0,z.useEffect)(()=>void W(),[_]);let Ae=re(`sm`),je=(0,z.useRef)(null),Me=p(),[Ne,Pe]=xe(`options`,Ce),G={},K=(e=r(`get_admins`).data)==null?void 0:e.list,q=R==null?void 0:R.connectionAddress,J=window.location.hostname===`localhost`,Fe=q==null?void 0:q.includes(`:`),Ie=[{label:E`any`,value:``,disabled:!1},{label:E`any IPv4`,value:`0.0.0.0`,disabled:!J&&(!q||Fe)},{label:E`any IPv6`,value:`::`,disabled:!J&&(!q||!Fe)},...[`127.0.0.1`,`::1`].map(e=>({label:e,value:e,disabled:!J&&q!==e})),...(R==null||(t=R.ips)==null?void 0:t.map(e=>({value:e,disabled:q!==e})))||[]];if(C)return C;if(I.error)return I.element;let Y=U===_?ae.config:A.config=U=_,Le={comp:k,min:1,unit:`KB/s`,placeholder:E`no limit`,sm:6},X={comp:k,placeholder:E`no limit`,toField:e=>e||``,sm:4},Z=Y[N.https_port]>=0;return(0,z.createElement)(oe,{sx:{maxWidth:`60em`},values:Y,set(e,t){A.config[t]=e},stickyBar:!0,onError:j,onValidation(e,t){if(!t)return;let n=e&&Object.keys(e).find(t=>e[t]);n&&G[n]!==void 0&&Pe(G[n])},save:M({children:E`Save`,ref:je,onClick:Q},ne(Object.keys(F).length>0)),barSx:{gap:2},addToBar:[(0,z.createElement)(l,{onClick(){y(),W()},startIcon:(0,z.createElement)(ee)},E`Reload`),(0,z.createElement)(l,{component:fe,href:`/config`,startIcon:(0,z.createElement)(be)},Ae?E`Config file`:E`File`)],defaults(){return{xs:6}},fields:Re({Net:[{k:N.port,comp:Ee,xs:12,sm:4,label:E`HTTP port`,status:(R==null?void 0:R.http)||!0,suggestedPort:80},{k:N.https_port,comp:Ee,xs:12,sm:4,label:E`HTTPS port`,status:(R==null?void 0:R.https)||!0,suggestedPort:443,onChange(e){return e>=0&&!Z&&!Y[N.cert]&&ke(),e}},{k:N.upnp_enabled,comp:L,xs:12,sm:4,label:E`UPnP/SSDP`,helperText:E`Port forwarding and double-NAT detection`},Z&&{k:N.cert,comp:T,sm:4,label:E`HTTPS certificate file`,helperText:P(`HTTPS#certificate`,E`What is this?`),error:D(R==null?void 0:R.https.error,e=>ue(e)&&(R.https.listening?e:[e,` - `,(0,z.createElement)(s,{key:`fix`,onClick:ke},E`make one`)]))},Z&&M({k:N.private_key,comp:T,sm:4,label:E`HTTPS private key file`},D(R==null?void 0:R.https.error,e=>u(e)?{error:!0,helperText:e}:null)),Z&&{k:N.force_https,comp:L,label:E`Force HTTPS`,sm:4,disabled:!Z||Y[N.port]<0,helperText:E`Not applied to localhost. Doesn't work with proxies.`},{k:N.listen_interface,comp:b,label:E`Listen interface`,sm:4,afterList:Ie.some(e=>e.disabled)&&(0,z.createElement)(w,{sx:{p:`8px 16px 0`,borderTop:`1px solid`,fontSize:`small`}},E`If an address is disabled, connect through it to select it`),options:Ie},M(M({k:N.max_kbps},Le),{},{sm:4,label:E`Limit output`,helperText:E`Doesn't apply to localhost`}),M(M({k:N.max_kbps_per_ip},Le),{},{sm:4,label:E`Limit output per-IP`}),M(M({k:N.max_downloads},X),{},{label:E`Max downloads`,helperText:E`Number of simultaneous downloads`}),M(M({k:N.max_downloads_per_ip},X),{},{label:E`Max downloads per-IP`}),M(M({k:N.max_downloads_per_account},X),{},{label:E`Max downloads per-account`,helperText:E`Overrides other limits`}),{k:N.admin_net,comp:x,xs:12,sm:6,label:E`Admin-panel accessible from`,placeholder:E`any address`,helperText:E`admin_net_localhost_hint`},{k:N.localhost_admin,comp:L,xs:12,sm:6,label:E`Consider localhost access as Admin`,getError:e=>!e&&(K==null?void 0:K.length)===0&&E`First create at least one admin account`,helperText:E`Access admin-panel without entering credentials`},{k:N.proxies,comp:k,xs:6,md:3,max:9,label:E`Number of incoming HTTP proxies`,placeholder:E`none`,error:ge(Y,R),helperText:E`Necessary to detect users' IP`},{k:N.outbound_interface,comp:b,xs:6,md:3,label:E`Outbound interface`,options:[{label:E`automatic`,value:``},...(R==null||(n=R.ips)==null?void 0:n.map(e=>({value:e})))||[]]},{k:N.outbound_proxy,xs:6,md:4,label:E`Outbound proxy`,placeholder:E`none`,helperText:E`URL form`,getError:e=>_e(()=>e&&new URL(e)&&``,()=>`Invalid URL`)},{k:N.allowed_referer,comp:De,md:2,placeholder:E`any`,label:E`Links from other websites`,helperText:E`In case another website is linking your files`},{k:N.block,label:!1,comp:v,xs:12,prepend:!0,sm:!0,autoRowHeight:!0,form:{sx:{maxWidth:`40em`}},fields:[{k:`ip`,label:E`Blocked IP`,sm:12,required:!0,wrap:!0,$width:2,comp:x,$column:{mergeRender:{comment:{},expire:{}}},helperText:E`block_self_warning`},{k:`expire`,label:E`Expire`,$type:`dateTime`,minDate:new Date,sm:6,$hideUnder:`sm`,helperText:E`Leave empty for no expiration`},{k:`disabled`,$type:`boolean`,label:E`Enabled`,helperText:E`geo_rule_disable_hint`,toField:e=>!e,fromField:e=>e?void 0:!0,sm:6,$width:80},{k:`comment`,label:E`Comment`,$hideUnder:`sm`}]}],"Front-end":[{k:N.file_menu_on_link,comp:b,label:E`Access file menu`,md:3,options:{[E`by clicking on file name`]:!0,[E`by dedicated button`]:!1}},{k:N.menu_at_top,comp:b,md:3,label:E`Menu panel`,options:{[E`at top`]:!0,[E`at bottom`]:!1}},{k:N.title,md:6,helperText:E`You can see this in the tab of your browser`},{k:N.auto_play_seconds,comp:k,xs:6,sm:3,min:1,max:1e4,required:!0,label:E`Auto-play seconds delay`,helperText:m(E(`show_interface_default_help`,{docsUrl:f+`discussions/270`}))},{k:N.tile_size,comp:k,xs:6,sm:3,max:10,required:!0,label:E`Default tiles size`,helperText:P(`Tiles`,E`To enable tiles-mode`)},{k:N.theme,comp:b,xs:6,sm:3,options:i},{k:N.sort_by,comp:b,xs:6,sm:3,options:h},{k:N.invert_order,comp:L,xs:6,md:3},{k:N.folders_first,comp:L,xs:6,md:3},{k:N.sort_numerics,comp:L,xs:6,md:3,label:E`Sort numeric names`},{k:N.title_with_path,comp:L,xs:6,md:3},{k:N.favicon,comp:T,placeholder:E`None`,fileMask:`*.ico|`+he,xs:12,sm:6,helperText:E`The icon associated to your website`},{k:N.show_uploader,label:E`Show uploader to`,comp:me,xs:!0},{k:N.page_size,comp:k,xs:!0,min:1,required:!0,helperText:E`Entries per page`}],Uploads:[{k:N.dont_overwrite_uploading,comp:L,md:4,label:E`Uploads don't overwrite`,helperText:E`Files are automatically numbered (frontend only)`},{k:N.split_uploads,comp:k,unit:`MB`,md:2,step:.1,min:.1,fromField:e=>e*1e6,toField:e=>e?e/1e6:null,placeholder:E`disabled`,label:E`Split uploads in chunks`,helperText:E`Overcome proxy limits (frontend only)`},{k:N.delete_unfinished_uploads_after,comp:k,md:3,min:0,unit:E`seconds`,required:!0,label:E`Delete unfinished uploads after`},{k:N.min_available_mb,comp:k,md:3,min:0,unit:`MBytes`,placeholder:E`None`,label:E`Min. available disk space`,helperText:E`Reject uploads that don't comply`},{k:N.own_upload_delete_hours,comp:k,xs:12,sm:6,md:3,min:0,unit:E`hours`,required:!0,label:E`Uploaders can delete their files for`,helperText:E`Set to 0 to disable`}],Others:[{k:N.show_hidden_files,comp:L,sm:3,label:E`Include hidden files`},{k:N.descript_ion_encoding,sm:3,label:E`Encoding of file DESCRIPT.ION`,comp:b,disabled:Y[N.comments_storage]===`attr`,options:[`utf8`,720,775,819,850,852,862,869,874,808,...B.default.range(1250,1257),10029,20866,21866]},{k:N.comments_storage,comp:b,xs:12,sm:6,options:{[E`in file DESCRIPT.ION`]:``,[E`in file attributes`]:`attr`,[E`in file attributes + load DESCRIPT.ION`]:`attr+ion`}},{k:N.keep_session_alive,comp:L,sm:6,md:6,helperText:E`frontend_page_open_session_hint`},{k:N.session_duration,comp:k,sm:3,md:3,min:5,unit:`seconds`,required:!0},{k:N.size_1024,label:E`KB size`,comp:b,sm:3,options:{1e3:!1,1024:!0}},{k:N.open_browser_at_start,comp:L,label:E`Open Admin-panel at start`,xs:12,sm:6,md:3,helperText:E`Browser is automatically launched with HFS`},{k:N.zip_calculate_size_for_seconds,comp:k,xs:12,sm:6,md:3,unit:`seconds`,required:!0,label:E`Calculate ZIP size for`,helperText:E`download_progress_time_hint`},{k:N.mime,comp:v,label:E`Custom MIME types`,reorder:!0,prepend:!0,xs:12,sm:12,md:6,fields:[{k:`v`,label:E`Mime type`,placeholder:E`auto`,$width:2,helperText:E`Leave empty to get automatic value`},{k:`k`,label:E`File mask`,helperText:(0,z.createElement)(S),$width:1,$column:{renderCell:({value:e,id:t})=>(0,z.createElement)(`code`,{},e,e===`*`&&t<B.default.size(Y[N.mime])-1&&c(pe,m(E`mime_order_hint`),{color:`warning.main`,ml:1}))}}],toField:e=>Object.entries(e||{}).map(([e,t])=>({k:e,v:t})),fromField:e=>Object.fromEntries(e.map(e=>[e.k,e.v||`auto`])),helperText:E`Most MIME types are detected automatically`},{k:N.force_webdav_login,comp:Oe,sm:!0,label:E`WebDAV force login`,fallbackRE:`Microsoft-WebDAV`,helperText:[E`force_login_compat_help`,P(`webdav`,E`Why?`)]},Y[N.force_webdav_login]!==!0&&{k:N.webdav_initial_auth,comp:Oe,sm:6,label:E`WebDAV initial auth`,helperText:E`force_login_once_help`},{k:N.server_code,comp:v,xs:12,fields:[{k:`name`,label:E`Name`,$width:.4,$render:({value:e})=>e.trim()||(0,z.createElement)(`i`,{},E`no name`)},{k:`code`,label:E`Code`,comp:de,lang:`js`,$hideUnder:`sm`,$render:({value:e})=>typeof e==`string`?(0,z.createElement)(ie,{},(0,H.highlight)(e,H.languages.js,`js`)):`?`,helperText:m(E(`server_code_plugin_help`,{docsUrl:f+`blob/main/dev-plugins.md`}))}],toField:e=>!e||typeof e!=`string`?[]:Se(e),fromField:ze,applyButton:{onClick:e=>Q({[N.server_code]:ze(e)})}}]})});function Re(e){let t=Object.entries(e),n=[];for(let[e,[,r]]of t.entries())for(let t of r){if(!t){n.push(t);continue}G[t.k]=e,n.push(M(M({},t),{},{parentProps:M(M({},t.parentProps),{},{hidden:e!==Ne})}))}return[{k:``,xs:12,comp:(0,z.createElement)(g,{value:Ne,variant:`scrollable`,scrollButtons:`auto`,onChange(e,t){Pe(t)}},t.map(([e])=>(0,z.createElement)(a,{key:e,label:E(e),sx:{minWidth:0}}))),parentProps:{ref:Me,sx:{position:`sticky`,top:{xs:56,sm:64,lg:0},zIndex:2,bgcolor:`background.paper`}}},...n]}function ze(e){return e.map(({name:e=``,code:t=``},n)=>n||e?`${V}${e}\n${t}\n`:t).join(``)}function Q(){return $.apply(this,arguments)}function $(){return $=ve(function*(e={}){var t,n;let r=M(M({},F),e);if(B.default.isEmpty(r))return se(E`Nothing to save`);let i=window.location,a=[N.port,N.https_port];if(a.every(e=>r[e]!==void 0))return j(E`change_both_ports_warning`,`warning`);let s=[R==null||(t=R.http)==null?void 0:t.listening,R==null||(n=R.https)==null?void 0:n.listening],c=location.protocol===`https:`;c&&(a.reverse(),s.reverse());let l=r[a[0]],u=Y[a[1]],d=u>0&&s[1],f=c?`http`:`https`;if(l<0&&!d)return j(E(`cannot_disable_only_fixed_port`,{otherProtocol:f}),`warning`);if(l===0&&!d)return j(E(`cannot_randomize_only_fixed_port`,{otherProtocol:f}),`warning`);let p=l>0&&l!=i.port;if(p&&!(yield O(E`You are changing the port and you may be disconnected`)))return;let m=N.cert in r||N.private_key in r;if(c&&m&&!(yield O(E`You may disrupt https service, kicking you out`)))return;yield ce(`set_config`,{values:r}),N.split_uploads in r&&(yield j(E`split_uploads_reload_notice`,`warning`));let h=te(i.hostname),g=i.pathname+i.hash,v=l<=0?`${c?`http:`:`https:`}//${h}:${u}${g}`:p?`${i.protocol}//${h}:${l||Y[a[0]]}${g}`:yield D(`https://${h}:${i.port}${g}`,e=>{var t;return!c&&((t=r[N.force_https])==null?_[N.force_https]:t)&&fetch(e).then(()=>e,()=>0)});if(v)return yield j(E`redirect_warning`,`warning`),window.location.href=v;let y=N.port in r||N.https_port in r;setTimeout(W,y||m?1e3:0),Object.assign(U,r),Te(),o(!1,je.current)}),$.apply(this,arguments)}}function Te(){let e={};if(A.config)for(let[t,n]of Object.entries(A.config))JSON.stringify(n)!==JSON.stringify(U==null?void 0:U[t])&&(e[t]=n);we.changes=e}function Ee({label:e,value:t,onChange:n,setApi:r,status:i,suggestedPort:a=1,error:o,helperText:s}){var c;let l=(0,z.useRef)(a);t>0&&(l.current=t);let d=Number(t>0?l.current:t)||0,f=i==null?void 0:i.error;return f&&(ue(f)||u(f)?f=void 0:o=!0),(0,z.createElement)(w,{},(0,z.createElement)(w,{sx:{display:`flex`}},(0,z.createElement)(b,{sx:{flexGrow:1},label:e,error:o,value:d,options:[{label:E`off`,value:-1},{label:E`random`,value:0},{label:E`choose`,value:l.current}],onChange:n}),t>0&&(0,z.createElement)(k,{label:E`Number`,fullWidth:!1,value:t,onChange:n,setApi:r,error:o,min:1,max:65535,helperText:s,sx:{minWidth:`5.5em`}})),i&&(0,z.createElement)(C,{error:o},i===!0?`...`:(c=f)==null?(i==null?void 0:i.listening)&&E(`Correctly working on port {port}`,{port:i.port}):c))}function De({label:e,value:t,onChange:r,error:i}){let a=!t||t===`-`,o=`example.com`;return(0,z.createElement)(w,{sx:{display:`flex`}},(0,z.createElement)(b,{label:e,value:a?t:o,options:{[E`allow all`]:``,[E`forbid all`]:`-`,[E`allow some`]:o},onChange:r,error:i,sx:a?void 0:{maxWidth:`11em`}}),!a&&(0,z.createElement)(n,{label:E`Domain to allow`,value:t,placeholder:`example.com`,onChange:r,error:i,helperText:(0,z.createElement)(S)}))}function Oe({label:e,value:t,onChange:r,error:i,helperText:a,fallbackRE:o=`.*`}){let[s,c]=(0,z.useState)(``),l=typeof t==`string`;(0,z.useEffect)(()=>c(l?t:o),[t]);let u=(0,z.useId)();return(0,z.createElement)(w,{},(0,z.createElement)(w,{sx:{display:`flex`}},(0,z.createElement)(b,{label:e,value:t,onChange:r,error:i,"aria-describedby":u,options:{[E`Off`]:!1,[E`Always`]:!0,[E`RegEx`]:s},sx:l?{maxWidth:`9em`}:void 0}),l&&(0,z.createElement)(n,{label:E`User-Agent regex`,value:t,onChange:r,error:i})),(0,z.createElement)(C,{id:u},a))}export{W as default};
|