hfs 3.3.4 → 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-jUH2feqi.js +4 -0
- package/admin/assets/CustomHtmlPage-Pl_AJZrl.js +2 -0
- package/admin/assets/InternetPage-DIcAwhFk.js +6 -0
- package/admin/assets/LangPage-BwVY8QRD.js +2 -0
- package/admin/assets/LogsPage-CcjS12cP.js +3 -0
- package/admin/assets/MonitorPage-DMVFXyMe.js +2 -0
- package/admin/assets/OptionsPage-BB40ax9G.js +3 -0
- package/admin/assets/PluginsPage-CC2dINcb.js +1 -0
- package/admin/assets/icons-C_ux1zLA.js +1 -0
- package/admin/assets/index-BSSQOMG_.js +755 -0
- package/admin/assets/index-mI9kZlpb.css +1 -0
- package/admin/assets/routing-Be72yFmd.js +1 -0
- package/admin/index.html +2 -2
- package/central.json +100 -1
- package/frontend/assets/{dist-legacy-CnYHQXZ6.js → dist-legacy-D7Dg5w3L.js} +1 -1
- package/frontend/assets/index-legacy-CnazC3Dj.js +1 -0
- package/frontend/assets/{lib-legacy-3xYueNHt.js → lib-legacy-BxFHpdER.js} +1 -1
- package/frontend/assets/{sha512-legacy-Cl3f_PhN.js → sha512-legacy-C8xoQBnP.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +9 -9
- package/package.json +2 -2
- package/plugins/antibrute/plugin.js +62 -15
- package/src/acme.js +143 -50
- package/src/acmeConfig.js +43 -0
- package/src/acmeDns.js +285 -0
- package/src/admin-langs/embedded.js +33 -0
- package/src/admin-langs/hfs-admin-lang-ar.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-bg.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-de.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-el.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-en.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-es.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-fi.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-fr.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-hu.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-it.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-ja.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-ko.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-lt.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-ms.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-nl.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-pl.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-pt-br.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-ro.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-ru.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-sr-latn.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-sr.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-th.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-tr.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-uk.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-vi.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-zh-hant.json +1149 -0
- package/src/admin-langs/hfs-admin-lang-zh.json +1149 -0
- package/src/adminApis.js +2 -2
- package/src/adminLang.js +51 -0
- package/src/api.auth.js +1 -1
- package/src/api.lang.js +22 -13
- package/src/api.log.js +5 -3
- package/src/api.net.js +20 -3
- package/src/api.plugins.js +53 -19
- package/src/api.vfs.js +3 -2
- package/src/argv.js +4 -0
- package/src/auth.js +13 -9
- package/src/basicWeb.js +6 -2
- package/src/block.js +1 -1
- package/src/config.js +1 -0
- package/src/connections.js +4 -7
- package/src/consoleLog.js +4 -0
- package/src/const.js +1 -1
- package/src/cross-const.js +8 -6
- package/src/cross.js +16 -2
- package/src/customHtml.js +3 -2
- package/src/frontEndApis.js +44 -7
- package/src/github.js +6 -37
- package/src/i18n.js +16 -4
- package/src/index.js +4 -3
- package/src/lang.js +21 -9
- package/src/langs/embedded.js +3 -2
- package/src/langs/hfs-lang-ar.json +7 -2
- package/src/langs/hfs-lang-bg.json +7 -2
- package/src/langs/hfs-lang-de.json +7 -2
- package/src/langs/hfs-lang-el.json +7 -2
- package/src/langs/hfs-lang-en.json +189 -0
- package/src/langs/hfs-lang-es.json +9 -4
- package/src/langs/hfs-lang-fi.json +8 -3
- package/src/langs/hfs-lang-fr.json +12 -7
- package/src/langs/hfs-lang-hu.json +7 -2
- package/src/langs/hfs-lang-it.json +11 -5
- package/src/langs/hfs-lang-ja.json +7 -2
- package/src/langs/hfs-lang-ko.json +7 -2
- package/src/langs/hfs-lang-lt.json +7 -2
- package/src/langs/hfs-lang-ms.json +9 -2
- package/src/langs/hfs-lang-nl.json +7 -2
- package/src/langs/hfs-lang-pl.json +7 -2
- package/src/langs/hfs-lang-pt-br.json +9 -4
- package/src/langs/hfs-lang-ro.json +7 -2
- package/src/langs/hfs-lang-ru.json +9 -4
- package/src/langs/hfs-lang-sr-latn.json +7 -2
- package/src/langs/hfs-lang-sr.json +7 -2
- package/src/langs/hfs-lang-th.json +8 -3
- package/src/langs/hfs-lang-tr.json +8 -3
- package/src/langs/hfs-lang-uk.json +16 -11
- package/src/langs/hfs-lang-vi.json +7 -2
- package/src/langs/hfs-lang-zh-hant.json +189 -0
- package/src/langs/hfs-lang-zh.json +97 -95
- package/src/listen.js +6 -3
- package/src/log.js +7 -1
- package/src/middlewares.js +87 -31
- package/src/multipartUpload.js +21 -0
- package/src/perm.js +38 -20
- package/src/pluginCatalog.js +66 -0
- package/src/pluginUsage.js +64 -0
- package/src/plugins.js +14 -2
- package/src/roots.js +22 -13
- package/src/serveFile.js +28 -11
- package/src/serveGuiAndSharedFiles.js +6 -6
- package/src/serveGuiFiles.js +7 -4
- package/src/update.js +51 -18
- package/src/upload.js +49 -8
- package/src/util-http.js +31 -5
- package/src/vfs.js +93 -4
- package/src/webdav.js +14 -8
- package/admin/assets/AccountsPage-CnZGcUox.js +0 -6
- package/admin/assets/ArrayField-F7fpCJIv.js +0 -2
- package/admin/assets/ConfigFilePage-CEgZ8Wq4.js +0 -4
- package/admin/assets/CustomHtmlPage-DDUNxF1H.js +0 -2
- package/admin/assets/DataTable-D_TeYxvk.js +0 -119
- package/admin/assets/DateTimeField-CsIdkTqz.js +0 -1
- package/admin/assets/EventManager--HiXIAY0.js +0 -1
- package/admin/assets/FileField-HsDCdr5S.js +0 -117
- package/admin/assets/InternetPage-DDwKrXJH.js +0 -8
- package/admin/assets/LangPage-Dd8hi_9K.js +0 -2
- package/admin/assets/LogsPage-BcPfugNY.js +0 -4
- package/admin/assets/MenuButton-cuehhVig.js +0 -1
- package/admin/assets/MonitorPage-Bre9d6MA.js +0 -2
- package/admin/assets/OptionsPage-Dd_cRDf4.js +0 -3
- package/admin/assets/PluginsPage-DJiseD4G.js +0 -1
- package/admin/assets/Tabs-C87E85Fh.js +0 -1
- package/admin/assets/TextEditor-BeS-mR2I.css +0 -1
- package/admin/assets/TextEditor-Cr0VI9Xi.js +0 -34
- package/admin/assets/VfsPathField-D1BkfhAM.js +0 -1
- package/admin/assets/agentIcons-CDoUTPUC.js +0 -1
- package/admin/assets/baseUrl-CnrV7Huu.js +0 -2
- package/admin/assets/icons-DJJYoK9E.js +0 -1
- package/admin/assets/index-11n5z0Ez.css +0 -1
- package/admin/assets/index-DwXlUHAs.js +0 -477
- package/admin/assets/routing-BQpoUdjq.js +0 -1
- package/frontend/assets/index-legacy-JYULclbY.js +0 -1
- package/src/langs/hfs-lang-zh-tw.json +0 -183
package/src/upload.js
CHANGED
|
@@ -5,7 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.dontOverwriteUploading = exports.minAvailableMb = exports.deleteUnfinishedUploadsAfter = void 0;
|
|
7
7
|
exports.getUploadMeta = getUploadMeta;
|
|
8
|
+
exports.saveUploadMeta = saveUploadMeta;
|
|
9
|
+
exports.setUploadApproved = setUploadApproved;
|
|
8
10
|
exports.getUploadTempFor = getUploadTempFor;
|
|
11
|
+
exports.isUploading = isUploading;
|
|
12
|
+
exports.whileUploadMetaPending = whileUploadMetaPending;
|
|
9
13
|
exports.uploadWriter = uploadWriter;
|
|
10
14
|
const vfs_1 = require("./vfs");
|
|
11
15
|
const const_1 = require("./const");
|
|
@@ -25,6 +29,7 @@ const expiringCache_1 = require("./expiringCache");
|
|
|
25
29
|
const first_1 = require("./first");
|
|
26
30
|
const uploadOwners_1 = require("./uploadOwners");
|
|
27
31
|
const webdav_1 = require("./webdav");
|
|
32
|
+
const adminApis_1 = require("./adminApis");
|
|
28
33
|
exports.deleteUnfinishedUploadsAfter = (0, config_1.defineConfig)(misc_1.CFG.delete_unfinished_uploads_after, 86_400);
|
|
29
34
|
exports.minAvailableMb = (0, config_1.defineConfig)(misc_1.CFG.min_available_mb, 100);
|
|
30
35
|
exports.dontOverwriteUploading = (0, config_1.defineConfig)(misc_1.CFG.dont_overwrite_uploading, true);
|
|
@@ -56,10 +61,18 @@ const ATTR_UPLOADER = 'uploader';
|
|
|
56
61
|
function getUploadMeta(path) {
|
|
57
62
|
return (0, misc_1.loadFileAttr)(path, ATTR_UPLOADER);
|
|
58
63
|
}
|
|
59
|
-
function
|
|
60
|
-
return (0, misc_1.storeFileAttr)(path, ATTR_UPLOADER,
|
|
64
|
+
function saveUploadMeta(path, meta) {
|
|
65
|
+
return (0, misc_1.storeFileAttr)(path, ATTR_UPLOADER, meta);
|
|
66
|
+
}
|
|
67
|
+
async function setUploadApproved(path, approved) {
|
|
68
|
+
const meta = await getUploadMeta(path);
|
|
69
|
+
return Boolean(meta && await saveUploadMeta(path, { ...meta, approved }));
|
|
70
|
+
}
|
|
71
|
+
function fillUploadMeta(path, ctx) {
|
|
72
|
+
return saveUploadMeta(path, {
|
|
61
73
|
username: (0, auth_1.getCurrentUsername)(ctx) || undefined,
|
|
62
74
|
ip: ctx.ip,
|
|
75
|
+
approved: Boolean((0, adminApis_1.ctxAdminAccess)(ctx)),
|
|
63
76
|
});
|
|
64
77
|
}
|
|
65
78
|
function getUploadTempFor(fullPath) {
|
|
@@ -67,6 +80,24 @@ function getUploadTempFor(fullPath) {
|
|
|
67
80
|
}
|
|
68
81
|
const diskSpaceCache = (0, expiringCache_1.expiringCache)(3_000); // invalidate shortly
|
|
69
82
|
const uploadingFiles = new Map();
|
|
83
|
+
const uploadMetaPending = new Set();
|
|
84
|
+
function isUploading(path) {
|
|
85
|
+
const key = (0, vfs_1.normalizeFilename)(path);
|
|
86
|
+
return (0, path_1.basename)(path).startsWith(const_1.UPLOAD_TEMP_PREFIX)
|
|
87
|
+
|| uploadingFiles.has(key)
|
|
88
|
+
|| uploadingFiles.has((0, vfs_1.normalizeFilename)(getUploadTempFor(path))) // active uploads are keyed by their temporary file
|
|
89
|
+
|| uploadMetaPending.has(key);
|
|
90
|
+
}
|
|
91
|
+
async function whileUploadMetaPending(path, cb) {
|
|
92
|
+
const key = (0, vfs_1.normalizeFilename)(path);
|
|
93
|
+
uploadMetaPending.add(key);
|
|
94
|
+
try {
|
|
95
|
+
return await cb();
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
uploadMetaPending.delete(key);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
70
101
|
// initially sync for formidable; still sync to avoid async races and PUT piping gaps
|
|
71
102
|
function uploadWriter(base, baseUri, filename, ctx) {
|
|
72
103
|
if (!filename || !(0, misc_1.isValidFileName)(filename) || !filename)
|
|
@@ -123,7 +154,7 @@ function uploadWriter(base, baseUri, filename, ctx) {
|
|
|
123
154
|
const createdFolders = [];
|
|
124
155
|
const firstCreated = !dir.endsWith(':\\') && fs_1.default.mkdirSync(dir, { recursive: true });
|
|
125
156
|
if (firstCreated) {
|
|
126
|
-
|
|
157
|
+
fillUploadMeta(dir, ctx);
|
|
127
158
|
// recursive mkdir returns the first created directory; existing ancestors must not receive ownership
|
|
128
159
|
const firstCreatedPath = (0, path_1.toNamespacedPath)((0, path_1.resolve)(firstCreated)); // Node 24 mkdir can return a namespace-prefixed Windows path
|
|
129
160
|
let source = (0, path_1.resolve)(dir);
|
|
@@ -219,12 +250,16 @@ function uploadWriter(base, baseUri, filename, ctx) {
|
|
|
219
250
|
await new Promise(res => fileStream.close(res)); // this only seems necessary on Windows
|
|
220
251
|
for (const { uri, source } of createdFolders)
|
|
221
252
|
await (0, uploadOwners_1.setUploadOwner)(uri, ctx, source);
|
|
222
|
-
if (errored)
|
|
223
|
-
return;
|
|
224
253
|
if (simulate)
|
|
225
254
|
return (0, promises_1.rm)(tempName).catch(() => { });
|
|
255
|
+
if (!await fillUploadMeta(tempName, ctx)) {
|
|
256
|
+
await (0, promises_1.rm)(tempName).catch(() => { });
|
|
257
|
+
return fail(const_1.HTTP_SERVER_ERROR);
|
|
258
|
+
}
|
|
259
|
+
if (errored)
|
|
260
|
+
return;
|
|
226
261
|
if (ctx.isAborted()) { // in the very unlikely case the connection is interrupted between last-byte and here, we still consider it unfinished, as the client had no way to know, and will resume, but it would get an error if we finish the process
|
|
227
|
-
const sec = exports.deleteUnfinishedUploadsAfter.get();
|
|
262
|
+
const sec = first_1.quitting ? undefined : exports.deleteUnfinishedUploadsAfter.get(); // preserve uploads interrupted by shutdown for resuming
|
|
228
263
|
await (0, uploadOwners_1.setUploadOwner)(tempOwnerUri, ctx, tempName, lodash_1.default.isNumber(sec) ? Date.now() + sec * 1000 : null);
|
|
229
264
|
return lodash_1.default.isNumber(sec) && delayedDelete(tempName, sec, tempOwnerUri);
|
|
230
265
|
}
|
|
@@ -254,11 +289,16 @@ function uploadWriter(base, baseUri, filename, ctx) {
|
|
|
254
289
|
() => (0, promises_1.rename)(tempName, dest).then(() => true, e => e?.code !== 'EBUSY' && Promise.reject(e)), { timeout: 10_000 });
|
|
255
290
|
if (!done)
|
|
256
291
|
throw 'EBUSY';
|
|
292
|
+
await (0, misc_1.moveStoredFileAttrs)(tempName, dest);
|
|
257
293
|
(0, uploadOwners_1.deleteUploadOwner)(tempOwnerUri); // the temp URI no longer exists after rename; final ownership is recorded below
|
|
258
294
|
if (mtime) // so we use it to touch the file
|
|
259
295
|
await (0, promises_1.utimes)(dest, Date.now() / 1000, mtime / 1000);
|
|
260
296
|
obj.fullPath = ctx.state.uploadDestinationPath = dest;
|
|
261
|
-
|
|
297
|
+
if (!await fillUploadMeta(dest, ctx)) {
|
|
298
|
+
await (0, promises_1.rename)(dest, tempName);
|
|
299
|
+
await (0, misc_1.moveStoredFileAttrs)(dest, tempName);
|
|
300
|
+
throw Error("couldn't store upload metadata");
|
|
301
|
+
}
|
|
262
302
|
if (ctx.query.comment)
|
|
263
303
|
void (0, comments_1.setCommentFor)(dest, String(ctx.query.comment));
|
|
264
304
|
obj.uri = (0, misc_1.enforceFinal)('/', baseUri) + (0, misc_1.pathEncode)((0, path_1.basename)(dest));
|
|
@@ -272,8 +312,9 @@ function uploadWriter(base, baseUri, filename, ctx) {
|
|
|
272
312
|
cb(obj);
|
|
273
313
|
}
|
|
274
314
|
catch (err) {
|
|
275
|
-
void
|
|
315
|
+
void fillUploadMeta(tempName, ctx);
|
|
276
316
|
console.error("Couldn't rename temp to", dest, String(err));
|
|
317
|
+
fail(const_1.HTTP_SERVER_ERROR);
|
|
277
318
|
}
|
|
278
319
|
}
|
|
279
320
|
finally {
|
package/src/util-http.js
CHANGED
|
@@ -38,6 +38,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.stream2string = void 0;
|
|
41
|
+
exports.readTextLimited = readTextLimited;
|
|
42
|
+
exports.readRequestBodyLimited = readRequestBodyLimited;
|
|
41
43
|
exports.httpString = httpString;
|
|
42
44
|
exports.httpWithBody = httpWithBody;
|
|
43
45
|
exports.httpStream = httpStream;
|
|
@@ -51,6 +53,29 @@ const consumers_1 = require("node:stream/consumers");
|
|
|
51
53
|
Object.defineProperty(exports, "stream2string", { enumerable: true, get: function () { return consumers_1.text; } });
|
|
52
54
|
const tls = __importStar(require("node:tls"));
|
|
53
55
|
const cross_1 = require("./cross");
|
|
56
|
+
const cross_const_1 = require("./cross-const");
|
|
57
|
+
async function readTextLimited(stream, maxBytes) {
|
|
58
|
+
if (Number(stream.headers['content-length']) > maxBytes)
|
|
59
|
+
return { error: 'too_large' };
|
|
60
|
+
const chunks = [];
|
|
61
|
+
let size = 0;
|
|
62
|
+
// keep the socket alive so callers can send the rejection before closing an undrained request
|
|
63
|
+
for await (const chunk of stream.iterator({ destroyOnReturn: false })) {
|
|
64
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
65
|
+
size += buffer.length;
|
|
66
|
+
if (size > maxBytes)
|
|
67
|
+
return { error: 'too_large' };
|
|
68
|
+
chunks.push(buffer);
|
|
69
|
+
}
|
|
70
|
+
return { text: new TextDecoder().decode(Buffer.concat(chunks, size)) };
|
|
71
|
+
}
|
|
72
|
+
async function readRequestBodyLimited(ctx, maxBytes) {
|
|
73
|
+
const body = await readTextLimited(ctx.req, maxBytes);
|
|
74
|
+
if (!('error' in body))
|
|
75
|
+
return body;
|
|
76
|
+
ctx.set('Connection', 'close'); // an unread rejected body makes this connection unsafe to reuse
|
|
77
|
+
ctx.status = cross_const_1.HTTP_PAYLOAD_TOO_LARGE;
|
|
78
|
+
}
|
|
54
79
|
async function httpString(url, { maxBytes = 10 * cross_1.MB, ...options } = {}) {
|
|
55
80
|
const stream = await httpStream(url, options);
|
|
56
81
|
if (maxBytes === Infinity)
|
|
@@ -85,12 +110,13 @@ function httpStream(url, { body, proxy, jar, noRedirect, httpThrow = true, ...op
|
|
|
85
110
|
options.headers['user-agent'] = httpStream.defaultUA;
|
|
86
111
|
if (body) {
|
|
87
112
|
options.method ||= 'POST';
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
if (!(body instanceof node_stream_1.Readable)) {
|
|
114
|
+
if (typeof body === 'object' && !(body instanceof Buffer)) {
|
|
115
|
+
options.headers['content-type'] ??= 'application/json';
|
|
116
|
+
body = JSON.stringify(body);
|
|
117
|
+
}
|
|
93
118
|
options.headers['content-length'] ??= Buffer.byteLength(body);
|
|
119
|
+
}
|
|
94
120
|
}
|
|
95
121
|
const { auth, ...parsed } = parseHttpUrl(url);
|
|
96
122
|
const hostJar = jar && (jar[parsed.hostname || ''] ||= {});
|
package/src/vfs.js
CHANGED
|
@@ -43,6 +43,7 @@ const walkDir_1 = require("./walkDir");
|
|
|
43
43
|
const node_stream_1 = require("node:stream");
|
|
44
44
|
const adminApis_1 = require("./adminApis");
|
|
45
45
|
const showHiddenFiles = (0, config_1.defineConfig)(misc_1.CFG.show_hidden_files, false);
|
|
46
|
+
const sourceNames = new Map();
|
|
46
47
|
function setVfsPath(node, vfsPath, parent) {
|
|
47
48
|
return (0, misc_1.setHidden)(node, {
|
|
48
49
|
vfsPath: (0, misc_1.enforceFinal)('/', parent?.vfsPath) + (0, misc_1.pathEncode)(vfsPath)
|
|
@@ -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;
|
|
@@ -190,6 +197,30 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
190
197
|
// does the tree node have a child that goes by this name, otherwise attempt disk
|
|
191
198
|
let virtualName = name;
|
|
192
199
|
let child = parent.children?.find(isSameFilenameAs(name));
|
|
200
|
+
const sameName = isSameFilenameAs(name);
|
|
201
|
+
const sourceName = Object.entries(parent.rename || {}).find(([, to]) => sameName(to))?.[0] || name;
|
|
202
|
+
if (!child) {
|
|
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;
|
|
207
|
+
if (!child && const_1.IS_WINDOWS && parent.source) {
|
|
208
|
+
const aliases = await windowsPathNames((0, path_1.join)(parent.source, sourceName), parent.source);
|
|
209
|
+
if (!aliases)
|
|
210
|
+
return;
|
|
211
|
+
child = findSourceChild(aliases);
|
|
212
|
+
if (!child) {
|
|
213
|
+
const normalized = new Set(aliases.map(normalizeFilename));
|
|
214
|
+
const candidates = parent.children?.filter(candidate => candidate.source
|
|
215
|
+
&& normalized.has(normalizeFilename((0, path_1.basename)(candidate.source)))) || [];
|
|
216
|
+
if (candidates.length > 1)
|
|
217
|
+
return; // an unverified physical alias must identify one VFS child
|
|
218
|
+
const [candidate] = candidates;
|
|
219
|
+
if (candidate?.source && await isSameFilePath((0, path_1.dirname)(candidate.source), parent.source))
|
|
220
|
+
child = candidate;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
193
224
|
if (child) { // found as vfs node
|
|
194
225
|
virtualName = getNodeName(child);
|
|
195
226
|
await setIsFolder(child); // in case it's pointing to a folder that didn't exist at loading time
|
|
@@ -203,7 +234,6 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
203
234
|
const ret = {};
|
|
204
235
|
let onDisk = name;
|
|
205
236
|
if (parent.rename) { // reverse the mapping
|
|
206
|
-
const sameName = isSameFilenameAs(name);
|
|
207
237
|
const entries = Object.entries(parent.rename);
|
|
208
238
|
// search display names first: with { A: B, C: A }, A must resolve to C rather than be hidden
|
|
209
239
|
const asDisplayName = entries.find(([, to]) => sameName(to));
|
|
@@ -212,12 +242,17 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
212
242
|
virtualName = asDisplayName[1];
|
|
213
243
|
}
|
|
214
244
|
else {
|
|
215
|
-
|
|
245
|
+
if (!await canonicalizeWindowsAlias())
|
|
246
|
+
return;
|
|
247
|
+
const sameSourceName = isSameFilenameAs(onDisk);
|
|
248
|
+
const asPhysicalName = entries.find(([from]) => sameSourceName(from));
|
|
216
249
|
if (asPhysicalName)
|
|
217
250
|
return; // a VFS rename replaces the original public name
|
|
218
251
|
}
|
|
219
252
|
ret.rename = renameUnderPath(parent.rename, onDisk);
|
|
220
253
|
}
|
|
254
|
+
else if (!await canonicalizeWindowsAlias())
|
|
255
|
+
return;
|
|
221
256
|
if (!(0, misc_1.isValidFileName)(onDisk))
|
|
222
257
|
return;
|
|
223
258
|
ret.source = (0, path_1.join)(parent.source, onDisk);
|
|
@@ -228,6 +263,22 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
228
263
|
if (assumeMissingToBeFolder)
|
|
229
264
|
ret.isFolder ??= true;
|
|
230
265
|
return ret;
|
|
266
|
+
async function canonicalizeWindowsAlias() {
|
|
267
|
+
if (!const_1.IS_WINDOWS || !name.includes('~'))
|
|
268
|
+
return true;
|
|
269
|
+
// Windows exposes 8.3 aliases that must keep the long name's VFS identity
|
|
270
|
+
const longPath = await convertWindowsPath((0, path_1.join)(parent.source, name), true);
|
|
271
|
+
if (!longPath)
|
|
272
|
+
return true; // new names containing '~' are still valid
|
|
273
|
+
if (!await isSameFilePath((0, path_1.dirname)(longPath), parent.source))
|
|
274
|
+
return false;
|
|
275
|
+
onDisk = virtualName = (0, path_1.basename)(longPath);
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function findSourceChild(names = [sourceName]) {
|
|
280
|
+
const normalized = new Set(names.map(normalizeFilename));
|
|
281
|
+
return parent.children?.find(x => getSourceNames(x, parent).some(source => normalized.has(normalizeFilename(source))));
|
|
231
282
|
}
|
|
232
283
|
}
|
|
233
284
|
function isCommentsFileName(name) {
|
|
@@ -267,8 +318,42 @@ async function reviewVfs() {
|
|
|
267
318
|
usedNames.add(normalizeFilename(getNodeName(child)));
|
|
268
319
|
}
|
|
269
320
|
await Promise.allSettled(node.children.map(recur));
|
|
321
|
+
await Promise.allSettled(node.children.map(child => setSourceNames(child, node)));
|
|
270
322
|
})(exports.vfs.compiled());
|
|
271
323
|
}
|
|
324
|
+
async function setSourceNames(node, parent) {
|
|
325
|
+
if (!node.source || !parent.source)
|
|
326
|
+
return;
|
|
327
|
+
const sameParent = normalizeFilename((0, path_1.dirname)((0, path_1.resolve)(node.source))) === normalizeFilename((0, path_1.resolve)(parent.source));
|
|
328
|
+
if (!sameParent && (!const_1.IS_WINDOWS || !await isSameFilePath((0, path_1.dirname)(node.source), parent.source)))
|
|
329
|
+
return;
|
|
330
|
+
const names = [(0, path_1.basename)(node.source)];
|
|
331
|
+
const key = sourceNamesKey(node, parent);
|
|
332
|
+
if (const_1.IS_WINDOWS) {
|
|
333
|
+
const converted = await Promise.all([convertWindowsPath(node.source), convertWindowsPath(node.source, true)]);
|
|
334
|
+
names.push(...converted.map(x => x && (0, path_1.basename)(x)).filter(Boolean));
|
|
335
|
+
}
|
|
336
|
+
sourceNames.set(key, lodash_1.default.uniq(names));
|
|
337
|
+
}
|
|
338
|
+
function convertWindowsPath(path, isLong = false) {
|
|
339
|
+
return new Promise(resolvePromise => {
|
|
340
|
+
if (!fswin_1.default.convertPath(path, resolvePromise, isLong))
|
|
341
|
+
resolvePromise(undefined);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
async function windowsPathNames(path, parent) {
|
|
345
|
+
const converted = await Promise.all([convertWindowsPath(path), convertWindowsPath(path, true)]);
|
|
346
|
+
const existing = converted.find(Boolean);
|
|
347
|
+
if (existing && !await isSameFilePath((0, path_1.dirname)(existing), parent))
|
|
348
|
+
return;
|
|
349
|
+
return lodash_1.default.uniq([(0, path_1.basename)(path), ...converted.map(x => x && (0, path_1.basename)(x)).filter(Boolean)]);
|
|
350
|
+
}
|
|
351
|
+
function getSourceNames(node, parent) {
|
|
352
|
+
return node.source && parent.source ? sourceNames.get(sourceNamesKey(node, parent)) || [] : [];
|
|
353
|
+
}
|
|
354
|
+
function sourceNamesKey(node, parent) {
|
|
355
|
+
return normalizeFilename((0, path_1.resolve)(parent.source)) + '\0' + normalizeFilename((0, path_1.resolve)(node.source));
|
|
356
|
+
}
|
|
272
357
|
exports.saveVfs = (0, misc_1.debounceAsync)(async () => {
|
|
273
358
|
await reviewVfs(); // refresh runtime-derived folder flags before saving mutated VFS state
|
|
274
359
|
exports.vfs.set(exports.vfs.compiled()); // sync the mutable runtime tree back to config state before persisting it
|
|
@@ -391,13 +476,16 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
391
476
|
started = true;
|
|
392
477
|
const { source } = parent;
|
|
393
478
|
const taken = new Set();
|
|
479
|
+
const takenSources = new Set();
|
|
394
480
|
const visitLater = [];
|
|
395
481
|
const childrenWorking = parent.children?.length && Promise.all(parent.children.map(async (child) => {
|
|
396
482
|
if (ctx?.isAborted())
|
|
397
483
|
return;
|
|
398
484
|
const nodeName = getNodeName(child);
|
|
399
485
|
const name = prefixPath + nodeName;
|
|
400
|
-
taken
|
|
486
|
+
taken.add(normalizeFilename(name));
|
|
487
|
+
for (const sourceName of getSourceNames(child, parent))
|
|
488
|
+
takenSources.add(normalizeFilename(prefixPath + sourceName));
|
|
401
489
|
const item = setVfsPath({ ...child, original: child, name, parent }, name, parent);
|
|
402
490
|
if (await cantSee(item, parent, nodeName))
|
|
403
491
|
return;
|
|
@@ -444,7 +532,8 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
444
532
|
? evaluatedParent.virtualPath + '/' + virtualName
|
|
445
533
|
: virtualName;
|
|
446
534
|
const name = prefixPath + virtualPath;
|
|
447
|
-
if (taken.has(normalizeFilename(name))
|
|
535
|
+
if (taken.has(normalizeFilename(name))
|
|
536
|
+
|| takenSources.has(normalizeFilename(prefixPath + path))) // taken by vfs node above
|
|
448
537
|
return false; // false just in case it's a folder
|
|
449
538
|
const item = setVfsPath({
|
|
450
539
|
name: virtualName,
|
package/src/webdav.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.webdav = void 0;
|
|
7
7
|
exports.releaseWebdavLock = releaseWebdavLock;
|
|
8
8
|
exports.isWebdavLocked = isWebdavLocked;
|
|
9
|
-
const
|
|
9
|
+
const util_http_1 = require("./util-http");
|
|
10
10
|
const vfs_1 = require("./vfs");
|
|
11
11
|
const cross_1 = require("./cross");
|
|
12
12
|
const stream_1 = require("stream");
|
|
@@ -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();
|
|
@@ -315,11 +315,14 @@ const webdav = async (ctx, next) => {
|
|
|
315
315
|
setWebdavHeaders(true);
|
|
316
316
|
return;
|
|
317
317
|
}
|
|
318
|
-
const body =
|
|
318
|
+
const body = await (0, util_http_1.readRequestBodyLimited)(ctx, 1024 * 1024);
|
|
319
|
+
if (!body)
|
|
320
|
+
return;
|
|
321
|
+
const bodyText = body.text;
|
|
319
322
|
const token = getProvidedLockToken();
|
|
320
323
|
let seconds = Number(ctx.get('timeout').split(',').find(x => /^Second-\d+$/i.test(x.trim()))?.trim().split('-', 2)[1]);
|
|
321
324
|
seconds = lodash_1.default.clamp(seconds || LOCK_DEFAULT_SECONDS, 1, LOCK_MAX_SECONDS);
|
|
322
|
-
if (!
|
|
325
|
+
if (!bodyText) {
|
|
323
326
|
// Finder and similar clients refresh an existing lock by sending LOCK without a body
|
|
324
327
|
if (!token)
|
|
325
328
|
return ctx.status = cross_1.HTTP_BAD_REQUEST;
|
|
@@ -338,7 +341,7 @@ const webdav = async (ctx, next) => {
|
|
|
338
341
|
ctx.body = renderLockResponse(lock.token, lock.seconds);
|
|
339
342
|
return;
|
|
340
343
|
}
|
|
341
|
-
const lockinfo = (0, cross_1.try_)(() => xmlParser.parse(
|
|
344
|
+
const lockinfo = (0, cross_1.try_)(() => xmlParser.parse(bodyText).lockinfo);
|
|
342
345
|
const scope = lodash_1.default.keys(lockinfo?.lockscope)[0];
|
|
343
346
|
const type = lodash_1.default.keys(lockinfo?.locktype)[0];
|
|
344
347
|
if (!scope || !type)
|
|
@@ -416,8 +419,11 @@ const webdav = async (ctx, next) => {
|
|
|
416
419
|
setWebdavHeaders(true);
|
|
417
420
|
return;
|
|
418
421
|
}
|
|
419
|
-
const body =
|
|
420
|
-
|
|
422
|
+
const body = await (0, util_http_1.readRequestBodyLimited)(ctx, 1024 * 1024);
|
|
423
|
+
if (!body)
|
|
424
|
+
return;
|
|
425
|
+
const bodyText = body.text;
|
|
426
|
+
const props = (0, cross_1.try_)(() => parseProppatchProps(bodyText)) || [];
|
|
421
427
|
if (!props.length)
|
|
422
428
|
return ctx.status = cross_1.HTTP_BAD_REQUEST;
|
|
423
429
|
const statuses = [];
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{t}from"./DateTimeField-CsIdkTqz.js";import{n,r,t as i}from"./MenuButton-cuehhVig.js";import{n as a,t as o}from"./VfsPathField-D1BkfhAM.js";import{Bn as s,C as c,Ct as l,Da as u,E as d,Ea as f,Et as p,F as m,Ft as h,H as g,Ir as _,Lt as v,M as y,Ma as b,N as x,O as S,Oa as C,Pa as w,R as T,S as ee,St as E,T as D,Ta as O,U as k,Ut as A,W as j,Wr as M,Yt as N,_ as te,_i as P,_r as ne,ai as F,en as I,fr as re,ft as L,gt as ie,ht as ae,it as R,j as z,jr as oe,ka as se,lr as B,lt as ce,mt as V,ni as le,nr as ue,q as de,ri as fe,st as pe,tr as me,tt as H,vi as U,w as he,y as W,zt as ge}from"./index-DwXlUHAs.js";import{$ as _e,J as ve,Q as ye,X as be,Y as xe,Z as Se,et as G,it as Ce,nt as we,rt as K,tt as Te}from"./icons-DJJYoK9E.js";var q=e(w());u();var Ee=[`password`,`password2`,`adminActualAccess`,`hasPassword`,`invalidated`,`canLogin`,`members`];function De({account:e,done:n,groups:r,addToBar:i,reload:a}){var u,m;let{username:h}=R(),[_,v]=(0,q.useState)(e),[x,S]=(0,q.useState)([]),C=I();(0,q.useEffect)(()=>{var t;v(e),S(r.filter(t=>t!==e.username)),C||(t=k.current)==null||(t=t.querySelector(`input`))==null||t.focus()},[JSON.stringify(e)]);let w=!e.username,{isGroup:T}=_,k=(0,q.useRef)(),{members:A}=e,j=(u=e.plugin)==null?void 0:u.auth;return(0,q.createElement)(ce,{key:e.username,formRef:k,values:_,set(e,t){v(n=>f(f({},n),{},{[t]:e}))},barSx:f({gap:2,width:`100%`},de()),stickyBar:!0,addToBar:[!w&&(0,q.createElement)(c,f(f({icon:K,title:`Delete`,confirm:`Delete ${e.username}?`},h===e.username&&{disabled:!0,title:`Cannot delete current account`}),{},{onClick:()=>B(`del_account`,{username:e.username}).then(a)})),(0,q.createElement)(c,{icon:xe,title:`Invalidate past sessions${s(`
|
|
2
|
-
(already invalidated sessions before `,E(e.invalidated||0),`)`)}`,confirm:`Invalidate all sessions up to now for "${e.username}"?`,doneMessage:!0,onClick:()=>B(`invalidate_sessions`,{username:e.username}).then(a)}),...me(i)],fields:[{k:`username`,label:T?`Group name`:void 0,autoComplete:`off`,required:!0,md:T&&!j?12:4,getError:t=>t!==e.username&&B(`get_account`,{username:t}).then(t=>(t==null?void 0:t.username)===e.username?`usernames are case-insensitive`:`already used`,()=>!1)},j&&{k:``,md:8,comp:(0,q.createElement)(U,{severity:`info`},` Authentication handled by a plugin`)},!T&&!j&&{k:`password`,xs:6,md:4,type:`password`,autoComplete:`new-password`,required:w,label:w?`Password`:`Change password`},!T&&!j&&{k:`password2`,xs:6,md:4,type:`password`,autoComplete:`new-password`,label:`Repeat password`,getError:(e,{values:t})=>(e||``)!==(t.password||``)&&`Enter same password`},{k:`disabled`,comp:L,fromField:e=>!e,toField:e=>!e,label:`Enabled`,xs:12,sm:6,lg:4,helperText:_.disabled||_.canLogin!==!1?`Login is prevented if account is disabled, or all its groups are disabled`:(0,q.createElement)(F,{sx:{color:`warning.main`},component:`span`},new Date(e.expire)<new Date?`Login is prevented because account is expired`:`Login is prevented because all of its groups are disabled`)},{k:`ignore_limits`,comp:L,xs:12,sm:6,lg:4,helperText:_.ignore_limits?`Speed limits don't apply to this account`:`Speed limits apply to this account`},f({k:`admin`,comp:L,fromField:e=>e||null,label:`Admin-panel access`,xs:12,sm:6,lg:4,helperText:`To access THIS interface you are using right now`},!e.admin&&e.adminActualAccess&&{value:!0,disabled:!0,helperText:`This permission is inherited. To disable it, act on the groups.`}),!T&&{k:`require_password_change`,comp:L,xs:12,sm:6,lg:4,helperText:`At next login, but can be dismissed`},{k:`disable_password_change`,label:`Password change`,comp:ie,xs:12,sm:6,lg:4,defaultValue:null,options:{[`Default (${_.canChangePassword?`Allowed`:`Disabled`})`]:null,Allowed:!1,Disabled:!0}},A?T&&!A.length?(0,q.createElement)(F,{},`No members`):A.length>0&&(0,q.createElement)(ee,{gap:0,flexWrap:`wrap`},`${A.length} members: `,N(`,\xA0`,(m=e.members)==null?void 0:m.map(e=>(0,q.createElement)(r.includes(e)?`i`:`span`,{},e))),(0,q.createElement)(W,{icon:K,confirm:`Delete ${e.members.length} accounts?`,onClick:()=>B(`del_account`,{username:e.members}).then(a),sx:{verticalAlign:`text-top`}})):null,T&&(0,q.createElement)(U,{severity:`info`},`To add users to this group, select the user and then click "Inherit"`),{k:`belongs`,comp:ae,label:`Inherit from groups`,options:x,sm:6,lg:4,helperText:`Specify groups to inherit permissions from`+(T?`. A group can inherit from another group`:``)+(x.length?``:`. Now disabled because there are no groups to select, create one first.`)},{k:`allow_net`,comp:d,label:`Allowed network address`,sm:6,lg:4,placeholder:`any address`},!T&&{k:`auto_login_net`,comp:d,label:`Auto-login by IP address`,sm:6,lg:4,placeholder:`none`,helperText:(0,q.createElement)(D,{onClick:()=>g(`IP addresses and localhost work without additional configuration.
|
|
3
|
-
If you access HFS using a hostname, configure it under Internet → Address, as the Main address or in Domain roots. Include the port if it appears in the address.
|
|
4
|
-
This prevents a malicious website from using your browser's authorized IP address to log in through DNS rebinding. Only configure domains you trust; avoid overly broad Domain roots wildcards.
|
|
5
|
-
For other hostnames, automatic login is skipped, but login with credentials remains available.`,{title:`Auto-login by IP address`})},`May require additional configuration`)},{k:`redirect`,comp:o,placeholder:`no`,sm:6,lg:4,helperText:`If you want this account to be redirected to a specific folder/address (or even file) at login time`},{k:`expire`,label:`Expiration`,sm:6,lg:4,comp:t,toField:e=>e&&new Date(e),helperText:`When expired, login won't be allowed`},f(f({k:`days_to_live`,sm:6,lg:4,comp:V,step:`any`,min:1/1e3},_.expire&&{xs:12,disabled:!0,sx:{opacity:.2}}),{},{helperText:`Used to set expiration on first login`}),{k:`notes`,multiline:!0,sm:6,lg:4}],onError:g,save:f(f({},y(l(_,e))),{},{onClick(){return b(function*(){var t;let{password:r=``,password2:i,adminActualAccess:a,hasPassword:o,invalidated:s,canLogin:c,members:l}=_,u=O(_,Ee),d=((t=k.current)==null?void 0:t.querySelector(`button.saveBtn`))||void 0;if(w){let e=yield B(`add_account`,u);if(r)try{yield p(_.username,r)}catch(e){throw B(`del_account`,{username:_.username}),e}n(e==null?void 0:e.username,d);return}let m=yield B(`set_account`,{username:e.username,changes:u});r&&(yield p(_.username,r),v(e=>f(f({},e),{},{password:``,password2:``}))),e.username===h&&(H.username=m.username),n(m==null?void 0:m.username,d)})()}})})}var J=e(re());se(),u();var Y={fieldSeparator:`,`,lineSeparator:`
|
|
6
|
-
`,quote:`"`},X={field:0,fieldOffset:0,line:0,lineOffset:0,quoted:!1,appendCell:!1,appendField:!1,appendRow:!1},Z=class extends Error{constructor(e,t){super(`Invalid CSV at ${e}:${t}`)}},Oe=class{constructor(e=Y){C(this,`_rows`,[]),C(this,`_row`,[]),C(this,`_cell`,``),C(this,`_options`,Y),C(this,`_state`,f({},X)),C(this,`_index`,0),C(this,`_current`,``),C(this,`_previous`,``),C(this,`_quoteState`,f({},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 Z(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=f({},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 Z(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=f({},X),this._index=0,this._current=``,this._previous=``,this._quoteState=f({},X)}};u();function ke(e){return Q.apply(this,arguments)}function Q(){return Q=b(function*(e){v(function(){var t=b(function*(t){let n=t==null?void 0:t[0];if(!n)return;let r=yield h(n);if(!r)return;let i=new Oe().parse(r.trim()),a={comp:V,min:1,max:9,xs:6,typing:!0},o={skipFirstLines:0,usernameColumn:1,passwordColumn:2,groupColumn:3,redirectColumn:4,overwriteExistingAccounts:!1},c=yield j({title:`Import accounts from CSV`,dialogProps:{maxWidth:`sm`},values:o,form:e=>{let t=i[e.skipFirstLines||0]||[],n=u(t,f(f({},o),e));return{save:{startIcon:(0,q.createElement)(ve),children:`Go`},fields:[(0,q.createElement)(F,{sx:{p:1}},`Total lines:`,i.length),{k:`skipFirstLines`,comp:V,max:i.length-1,typing:!0,md:6,getError:e=>e!=null&&!Number.isInteger(e)&&`Enter an integer`,helperText:(0,q.createElement)(q.Fragment,{},`First line: `,(0,q.createElement)(`code`,{},t.join(`, `)))},{k:`overwriteExistingAccounts`,comp:L,md:6},f(f({k:`usernameColumn`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},`First username: `,n.u)}),f(f({k:`passwordColumn`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},`First password: `,n.p)}),f(f({k:`groupColumn`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},`First group: `,n.g)}),f(f({k:`redirectColumn`},a),{},{helperText:(0,q.createElement)(q.Fragment,{},`First redirect: `,n.r)})]}}});if(!c)return;let{close:l}=A({title:`Importing...`,Content(){let[t,n]=(0,q.useState)(0),[r,a]=(0,q.useState)();return(0,q.useEffect)(()=>{let t=!1;return setTimeout(b(function*(){if(t)return;let r=0,o=0,d=c.skipFirstLines,f=i.length-d,m=0;try{for(let e of i){var h;if(t)return;if(d){d--;continue}let i=u(e,c);a(i),n(m++/f),yield B(`add_account`,{username:i.u,belongs:(h=i.g)==null?void 0:h.split(`,`),redirect:i.r,overwrite:c.overwriteExistingAccounts}).then(()=>{if(i.p)return p(i.u,i.p)}).catch(e=>{if(e.code===409)return o++;r++})}}finally{l();let t=m-r-o;g(`Results: `+[s(``,r,` failed`),s(``,t,` succeeded`),s(``,o,` skipped because already present`)].filter(Boolean).join(`, `),!t&&r?`error`:r||o?`warning`:`success`),e==null||e()}})),()=>{t=!0}},[]),(0,q.createElement)(F,{sx:{display:`flex`,gap:2,alignItems:`center`}},(0,q.createElement)(he,{icon:G,progress:t}),r==null?void 0:r.u)}});function u(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`})}),Q.apply(this,arguments)}u();var Ae=` `,$=e=>e==null?void 0:e.split(Ae).at(-1);function je(){let{username:e,accountsAsTree:t}=R(),{data:o,reload:s,element:l}=te(`get_accounts`),[u,d]=(0,q.useState)([]),p=Array.isArray(u);(0,q.useEffect)(()=>{Array.isArray(o==null?void 0:o.list)&&p&&d(u.filter(e=>o.list.find(t=>(t==null?void 0:t.username)===$(e))))},[o]);let h=(0,q.useMemo)(()=>o&&J.default.sortBy(o.list,[e=>!e.isGroup,e=>!e.adminActualAccess,`username`]),[o]),v=p&&J.default.find(h,{username:$(u[0])}),y=m(`md`),C=!(u.length>0)||!h?null:p&&u.length>1?(0,q.createElement)(q.Fragment,{},(0,q.createElement)(ee,{},(0,q.createElement)(P,{variant:`h6`},u.length+` selected`),(0,q.createElement)(W,{onClick:N,icon:K},`Remove`)),(0,q.createElement)(_,{},J.default.uniq(u.map($)).map(e=>(0,q.createElement)(a,{key:e},(0,q.createElement)(oe,{},e))))):ue(v||O(),e=>(0,q.createElement)(De,{account:e,groups:h.filter(e=>e.isGroup).map(e=>e.username),addToBar:y&&[(0,q.createElement)(F,{sx:{flex:1}}),Me(e,{fontSize:`large`,sx:{p:1}}),(0,q.createElement)(c,{icon:pe,title:`Close`,onClick:j})],reload:s,done(e,t){d(y?[e]:[]),s(),S(``,t)}}));(0,q.useEffect)(()=>{if(y||!C||!u.length)return;let{close:e}=A({title:J.default.isString(u)?J.default.startCase(u):u.length>1?`Multiple selection`:v?(v.isGroup?`Group: `:`User: `)+v.username:`?`,Content:()=>C,onClose(e){e||j()}});return()=>void e(!0)},[y,u,v]);let w={height:`100%`,display:`flex`,flexDirection:`column`,overflow:`auto`},[E,D]=T(`Show tree`,`Show list`,()=>({icon:ne}),t);return H.accountsAsTree=E,l||(0,q.createElement)(M,{container:!0,sx:{rowSpacing:1,columnSpacing:2,top:0,flex:`1 1 auto`,height:0}},(0,q.createElement)(M,{size:{xs:12,md:5,lg:4,xl:5},sx:w},(0,q.createElement)(F,{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)(i,{variant:`contained`,startIcon:(0,q.createElement)(Se),items:[{children:`user`,onClick:()=>d(`new-user`)},{children:`group`,onClick:()=>d(`new-group`)},{children:`from CSV`,onClick:()=>ke(s)}]},`Add`),x(s),D,(h==null?void 0:h.length)>0&&(0,q.createElement)(P,{sx:{p:1}},`${h.length} account(s)`)),!(h!=null&&h.length)&&(0,q.createElement)(U,{severity:`info`},ge`To access administration <u>remotely</u> you will need to create a user account with admin permission`),(0,q.createElement)(r,{multiSelect:!0,sx:{pr:4,pb:2,minWidth:`15em`},selectedItems:p?u:[],slots:{collapseIcon:Te,expandIcon:Ce},onSelectedItemsChange(e,t){var n,r;!(e==null||(n=e.target)==null||(r=n.closest)==null)&&r.call(n,`.MuiTreeItem-iconContainer`)||d(t)}},h&&(function e(t,r=``){return t.map(t=>{var i,a;return(0,q.createElement)(n,{key:t.username,itemId:r+t.username,label:(0,q.createElement)(F,{sx:{display:`flex`,flexWrap:`wrap`,padding:`.2em 0`,columnGap:`.5em`,alignItems:`center`}},Me(t),(t.disabled||t.canLogin===!1)&&z(we,t.disabled?`Disabled`:`Disabled by its groups`,t.disabled?void 0:{color:`text.secondary`}),(t.expire||t.days_to_live)&&(0,q.createElement)(be),t.adminActualAccess&&z(_e,`Can login into Admin`),t.username,!!((i=t.belongs)!=null&&i.length)&&(0,q.createElement)(F,{sx:{color:`text.secondary`,fontSize:`small`}},`(`,(a=t.belongs)==null?void 0:a.join(`, `),`)`))},E&&e(h.filter(e=>{var n;return(n=t.directMembers)==null?void 0:n.includes(e.username)}),r+t.username+Ae))})})(E?h.filter(e=>!h.some(t=>{var n;return(n=t.members)==null?void 0:n.includes(e.username)})):h))),y&&C&&(0,q.createElement)(M,{size:`grow`,sx:f(f({},w),{},{maxWidth:`100%`})},(0,q.createElement)(fe,{sx:{overflow:`initial`}},(0,q.createElement)(le,{},C))));function O(){return{username:``,hasPassword:u===`new-user`,adminActualAccess:!1,invalidated:void 0,canLogin:!0,canChangePassword:!0,isGroup:u===`new-group`,members:[],directMembers:[]}}function j(){d([])}function N(){return I.apply(this,arguments)}function I(){return I=b(function*(){if(typeof u==`string`||u.some(t=>$(t)===e)&&!(yield k(`You cannot ask to delete the account you are using. Continue with the rest?`)))return;let t=J.default.without(J.default.uniq(u.map($)),e);if(!t.length)return g(`Nothing to delete`,`info`);if(!(yield k(`Delete ${t.length} item(s)?`)))return;let n=[];for(let e of t)(yield B(`del_account`,{username:e}).then(()=>1,()=>0))||n.push(e);if(s(),n.length)return g(`The following items couldn't be deleted: `+n.join(`, `),`error`)}),I.apply(this,arguments)}}function Me(e,t={}){return(0,q.createElement)(e.isGroup?G:ye,t)}export{je as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{n as t,t as n}from"./DataTable-D_TeYxvk.js";import{t as r}from"./DateTimeField-CsIdkTqz.js";import{Aa as i,C as a,Da as o,Ea as s,F as c,Gr as l,I as u,Kr as d,Ma as f,Pa as p,S as m,St as h,Ta as g,W as _,Wn as v,Yn as y,ai as b,b as ee,ct as x,dt as S,fr as C,ft as w,ii as T,ja as E,un as D,ut as O,wn as te}from"./index-DwXlUHAs.js";import{F as ne,I as re,L as ie,P as ae,q as oe,rt as se}from"./icons-DJJYoK9E.js";var k=e(p()),A=e(C());o(),i();var j=[`label`,`helperText`,`fields`,`value`,`onChange`,`onError`,`setApi`,`reorder`,`prepend`,`noRows`,`valuesForAdd`,`autoRowHeight`,`dialog`,`form`,`details`,`objectK`,`saveOn`,`applyButton`,`height`,`error`,`sx`],M=[`$width`,`$column`,`$type`,`$hideUnder`,`showIf`,`$render`,`$mergeRender`];function N(e){let{label:r,helperText:i,fields:o,value:u,onChange:p,onError:h,setApi:x,reorder:C,prepend:w,noRows:T,valuesForAdd:N,autoRowHeight:I,dialog:L,form:R,details:z,objectK:B,saveOn:V,applyButton:H,height:U,error:W,sx:G}=e,ce=g(e,j),K=Array.isArray(u)?u:!B||!u?[]:Object.entries(u).map(([e,t])=>s({[B]:e},t)),q=(0,k.useMemo)(()=>K.map((e,t)=>v(s({},e),e.hasOwnProperty(`id`)?{$idx:t}:{id:t})),[JSON.stringify(K)]),J=!!W||void 0,Y=e=>t=>s(s({fields:D(o,t).map(e=>{var n;let{$width:r,$column:i,$type:a,$hideUnder:o,showIf:s,$render:c,$mergeRender:l}=e,u=g(e,M);return(!s||s(t))&&A.default.defaults(u,(n=F[a])==null?void 0:n.field)})},R),e);x==null||x({isEqual:te});let[X,le]=(0,k.useState)();return(0,k.createElement)(k.Fragment,{},(0,k.createElement)(m,{rowGap:0,flexWrap:`wrap`,ml:`2px`},r&&(0,k.createElement)(l,{error:J,sx:{color:J?void 0:`text.primary`}},r),i&&(0,k.createElement)(d,{error:J},i)),(0,k.createElement)(b,s(s({},ce),{},{sx:S({height:U},G)}),(0,k.createElement)(n,s(s(s({rows:q,details:z},I&&{getRowHeight:()=>`auto`}),!c(`sm`)&&{compact:!0}),{},{sx:s({".MuiDataGrid-virtualScroller":{minHeight:`3em`}},I&&{".MuiDataGrid-cell":{minHeight:`52px !important`}}),hideFooterSelectedRowCount:!0,hideFooter:!0,slots:{noRowsOverlay:()=>(0,k.createElement)(ee,{},T||`No entries`)},slotProps:{pagination:{showFirstButton:!0,showLastButton:!0}},onCellDoubleClick({field:e,row:t},n){var r;let i=D(o,!1).find(t=>!(0,k.isValidElement)(t)&&t.k===e&&t.$type===`boolean`);if(!i)return;let a=(r=t.$idx)==null?t.id:r,c=t[e],l=!(i.toField||A.default.identity)(c);Z(a,s(s({},K[a]),{},{[e]:(i.fromField||A.default.identity)(l,{originalValue:c})}),n)},columns:[...D(o,!1).map(e=>{var t,n;if((0,k.isValidElement)(e))return;let r=(t=F[e.$type])==null?void 0:t.column;return A.default.defaults(s(s({field:e.k,headerName:(n=e.headerName)==null?typeof e.label==`string`?e.label:O(e.k):n,disableColumnMenu:!0,valueGetter:t=>(e.toField||A.default.identity)(t)},e.$width?{[e.$width>=8?`width`:`flex`]:e.$width}:!(r!=null&&r.width)&&!(r!=null&&r.flex)&&{flex:1}),{},{renderCell:e.$render,mergeRender:e.$mergeRender,hideUnder:e.$hideUnder},e.$column),r)}).filter(Boolean),{field:``,type:`actions`,width:90,headerAlign:`center`,renderHeader(){return(0,k.createElement)(k.Fragment,{},(0,k.createElement)(a,{icon:ie,title:`Add`,size:`small`,onClick(e){return f(function*(){let t=yield _({form:Y({}),title:`Add`,values:N,dialogProps:L});if(!t)return;let n=K.slice()||[];w?n.unshift(t):n.push(t),Q(n,e)})()}}),X!==void 0&&(0,k.createElement)(a,{icon:ae,title:`Undo`,size:`small`,onClick:e=>Q(X,e)}))},getActions({row:e}){let{$idx:n=e.id}=e,i=`Modify`;return[(0,k.createElement)(t,{key:`edit`,icon:(0,k.createElement)(oe),label:i,title:i,onClick(t){return f(function*(){t.stopPropagation();let a=yield _({values:e,form:(e,{submit:t})=>Y(V===`change`&&{save:!1,onChange(e){Z(n,e,`change`)}}||H&&{addToBar:(0,k.createElement)(P,s(s({children:`Apply`,sx:s({ml:2},H.sx)},H),{},{onClick(){t(function(){var e=f(function*(e){let t=Z(n,s(s({},K[n]),e),`save`);yield H.onClick(t)});return function(t){return e.apply(this,arguments)}}())}}))}||V&&{save:!1})(e),title:(0,k.createElement)(k.Fragment,{},i,r&&` - `,r),dialogProps:L});a&&Z(n,a,t)})()}}),(0,k.createElement)(t,{key:`delete`,icon:(0,k.createElement)(se),label:`Delete`,showInMenu:C,onClick(e){e.stopPropagation(),Q(K.filter((e,t)=>t!==n),e)}}),C&&n&&(0,k.createElement)(t,{key:`up`,icon:(0,k.createElement)(re),label:`Move up`,showInMenu:!0,onClick(e){e.stopPropagation(),Q(y(K.slice(),n,n-1),e)}}),C&&n<q.length-1&&(0,k.createElement)(t,{key:`down`,icon:(0,k.createElement)(ne),label:`Move down`,showInMenu:!0,onClick(e){e.stopPropagation(),Q(y(K.slice(),n,n+1),e)}})].filter(Boolean)}}]}))));function Z(e,t,n){return Q(K.map((n,r)=>r===e?t:n),n)}function Q(e,t){let n=$(e);return p(n,{was:u,event:t}),V!==`change`&&le(K),n}function $(e){return B?Object.fromEntries(e.map(e=>{let{[B]:t}=e;return[t,g(e,[B].map(E))]})):e}}function P(e){return(0,k.createElement)(T,s({ref:u([`s`]).ref,title:`Apply
|
|
2
|
-
(ctrl+s)`},e))}var F={boolean:{field:{comp:w},column:{renderCell:({value:e})=>e&&(0,k.createElement)(x)}},dateTime:{field:{comp:r},column:{minWidth:96,flex:.5,renderCell:({value:e})=>h(e)}}};export{N as t};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{Bn as t,C as n,Jt as r,N as i,Nt as a,Pa as o,S as s,_ as c,ai as l,jt as u,lr as d,pt as f,qt as p,tt as m,vi as h,vt as g,y as _}from"./index-DwXlUHAs.js";import{K as v,q as y}from"./icons-DJJYoK9E.js";import{t as b}from"./TextEditor-Cr0VI9Xi.js";var x=e(o());function S(){m.title=`Config file`;let{data:e,reload:o,element:S}=c(`get_config_text`,{}),[C,w]=(0,x.useState)(),[T,E]=(0,x.useState)(),[D,O]=(0,x.useState)(!1);return(0,x.useEffect)(()=>{E(e==null?void 0:e.text),w(e==null?void 0:e.text)},[e]),S||(0,x.createElement)(x.Fragment,{},(0,x.createElement)(s,{flexWrap:`wrap`,justifyContent:`space-between`},(0,x.createElement)(_,{icon:v,onClick:A,disabled:!e},`Export without passwords`),D?(0,x.createElement)(x.Fragment,{},i(o),(0,x.createElement)(n,{icon:g,title:`Save
|
|
2
|
-
(ctrl+enter)`,modified:C!==T,onClick:k}),(0,x.createElement)(h,{severity:`warning`,sx:{minWidth:`10em`}},`Be careful, you can easily break things here`)):(0,x.createElement)(_,{icon:y,variant:`outlined`,onClick(){O(!0),setTimeout(()=>a(`main textarea`),500)}},`Edit`),(0,x.createElement)(l,{sx:{flex:1,minWidth:`fit-content`}},(0,x.createElement)(f,{label:`File path`,value:e==null?void 0:e.fullPath,size:`small`}))),S||C!==void 0&&(0,x.createElement)(l,{sx:{"& pre,& textarea":{wordBreak:`break-all !important`}}},(0,x.createElement)(p,{margin:10},(0,x.createElement)(b,{value:C,disabled:!D,style:{background:`#8881`},onValueChange:w,onKeyDown(e){[`s`,`Enter`].includes(r(e))&&(k(),e.preventDefault())}}))));function k(){return d(`set_config_text`,{text:C}).then(()=>E(C))}function A(){var n;let r=(C||``).replace(/^(\s*(\w*password|_\w*|token|srp|dynamic_dns_url):\s*).+(?:\r?\n|$)/gm,`$1removed
|
|
3
|
-
`).replace(/(:\/\/)[^/@\s]+@/g,`$1removed@`)+t(`custom_html: | # this is currently ignored by hfs, just here for reference
|
|
4
|
-
`,(n=e.customHtml)==null?void 0:n.replace(/^/gm,` `));r&&u(`config_no_passwords.yaml`,r)}}export{S as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{A as t,Bn as n,C as r,Cn as i,Da as a,Ea as o,H as s,I as c,Ma as l,N as u,Pa as d,_ as f,ai as p,fr as m,gt as h,ir as g,it as _,lr as v,na as y,or as b,tt as x,vi as S,vt as C,xr as w,z as T,zt as E}from"./index-DwXlUHAs.js";import{t as D}from"./TextEditor-Cr0VI9Xi.js";var O=e(m()),k=e(d());a();function A(e,t,n){return(0,k.createElement)(j,o({checked:e||!1,onChange(e,n){t(n)},disabled:e===void 0},n))}var j=y(w)(({theme:e,size:t})=>{let n=t&&t===`small`;return{height:n?36:48,width:n?18:26,padding:5,"& .MuiSwitch-switchBase":{margin:1,padding:0,transform:`translateY(${n?16:22}px)`,"&.Mui-checked":{color:e.palette.primary.contrastText,transform:`translateY(2px)`,"& .MuiSwitch-thumb:before":{backgroundImage:`url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="${n?14:20}" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(e.palette.primary.contrastText)}" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/></svg>')`},"& + .MuiSwitch-track":{backgroundColor:e.palette.grey[500]}}},"& .Mui-checked .MuiSwitch-thumb":{backgroundColor:e.palette.primary.main,boxShadow:`0px 2px 1px 1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12)`},"& .MuiSwitch-thumb":{width:n?16:24,height:n?16:24,backgroundColor:e.palette.grey[700],"&::before":{content:`''`,position:`absolute`,width:`85%`,height:`85%`,left:0,top:0,backgroundRepeat:`no-repeat`,backgroundPosition:`center`,backgroundImage:`url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="${n?14:20}" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(e.palette.primary.contrastText)}" d="M19,13H5V11H19V13Z"/></svg>')`}},"& .MuiSwitch-track":{backgroundColor:e.palette.grey[500],borderRadius:20/2}}});a();var M={top:`Top of HTML Body`,bottom:`Bottom of HTML Body`};function N({setTitleSide:e}){let{data:a,reload:d,element:m}=f(`get_custom_html`),{customHtmlSection:y}=_(),[w,j]=(0,k.useState)({}),[N,P]=(0,k.useState)({});(0,k.useEffect)(()=>{a&&(P(a.sections),j(a.sections),R(a.enabled))},[a]);let F=(0,k.useMemo)(()=>{let e=O.default.sortBy(Object.keys(w),i);return e.length&&!e.includes(y)&&(x.customHtmlSection=O.default.findKey(w,Boolean)||(e==null?void 0:e[0])||``),e.map(e=>{var t;return{value:e,label:(M[e]||n(`HTTP `,b[e])||O.default.startCase(e))+((t=w[e])!=null&&t.trim()?` *`:``)}})},[w]),I=(0,k.useMemo)(()=>!O.default.isEqualWith(N,w,(e,t)=>!e&&!t||void 0),[N,w]),[L,R]=(0,k.useState)();e((0,k.useMemo)(()=>(0,k.createElement)(p,{sx:{display:{xs:`none`,md:`block`}}},(0,k.createElement)(S,{severity:`info`},E("Add HTML code to some parts of the Front-end. It's saved to file `custom.html`, that you can edit directly with your editor of choice. "),T(`customization`,`More help`)),(0,k.createElement)(S,{severity:`info`},`To customize icons `,T(`customization#icons`,`read documentation`))),[]));let z=c([`s`,`Enter`]);return m||(0,k.createElement)(k.Fragment,{},(0,k.createElement)(p,{sx:{display:`flex`,alignItems:`center`,gap:1,mb:1}},(0,k.createElement)(h,{label:`Section`,value:y,options:F,onChange:e=>x.customHtmlSection=e}),u(d),(0,k.createElement)(r,{ref:z.ref,icon:C,title:`Save
|
|
2
|
-
(ctrl+s)`,modified:I,doneAnimation:!0,onClick:B}),t(`Enable all sections`,void 0,A(L,function(){var e=l(function*(e){try{yield v(`set_config`,{values:{[g.disable_custom_html]:!e}}),R(e)}catch(e){s(e)}});return function(t){return e.apply(this,arguments)}}()))),(0,k.createElement)(D,{lang:y===`style`?`css`:y===`script`?`js`:`html`,value:(w==null?void 0:w[y])||``,style:{background:`#8881`},onValueChange(e){j(t=>o(o({},t),{},{[y]:e}))}}));function B(){return v(`set_custom_html`,{sections:w}).then(()=>P(w))}}export{N as default};
|