hfs 3.3.3 → 3.4.0-alpha1
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-C6J2hRcY.js +4 -0
- package/admin/assets/ConfigFilePage-D5TZZyMR.js +4 -0
- package/admin/assets/CustomHtmlPage-DV2-ufWZ.js +2 -0
- package/admin/assets/InternetPage-DT3wSQYQ.js +6 -0
- package/admin/assets/LangPage-DbXCv7wR.js +2 -0
- package/admin/assets/LogsPage-DmAxUZpu.js +3 -0
- package/admin/assets/MonitorPage-D7ydOeq_.js +2 -0
- package/admin/assets/OptionsPage-DpN3ajRm.js +3 -0
- package/admin/assets/PluginsPage-BYzfwuvg.js +1 -0
- package/admin/assets/icons-CfBG9vvB.js +1 -0
- package/admin/assets/index-CVCnYMoB.js +755 -0
- package/admin/assets/index-mI9kZlpb.css +1 -0
- package/admin/assets/routing-C4l8rnk_.js +1 -0
- package/admin/index.html +2 -2
- package/central.json +100 -1
- package/frontend/assets/{dist-legacy-CSuZ0eoA.js → dist-legacy-CKczq_fx.js} +1 -1
- package/frontend/assets/index-legacy-ZYae30AY.js +1 -0
- package/frontend/assets/{lib-legacy-I77jyH6j.js → lib-legacy-D2tyILlj.js} +1 -1
- package/frontend/assets/{sha512-legacy-DxvM1xc4.js → sha512-legacy-CFcDeiiP.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +2 -2
- 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 +1141 -0
- package/src/admin-langs/hfs-admin-lang-bg.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-de.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-el.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-en.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-es.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-fi.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-fr.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-hu.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-it.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-ja.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-ko.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-lt.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-ms.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-nl.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-pl.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-pt-br.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-ro.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-ru.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-sr-latn.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-sr.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-th.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-tr.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-uk.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-vi.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-zh-tw.json +1141 -0
- package/src/admin-langs/hfs-admin-lang-zh.json +1141 -0
- package/src/adminApis.js +2 -2
- package/src/adminLang.js +50 -0
- 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/argv.js +4 -0
- package/src/auth.js +0 -2
- 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/const.js +1 -1
- package/src/cross-const.js +7 -5
- package/src/cross.js +15 -1
- package/src/frontEndApis.js +44 -7
- package/src/github.js +6 -37
- package/src/i18n.js +16 -4
- package/src/index.js +4 -2
- package/src/lang.js +15 -8
- package/src/langs/embedded.js +2 -1
- package/src/langs/hfs-lang-ar.json +5 -1
- package/src/langs/hfs-lang-bg.json +5 -1
- package/src/langs/hfs-lang-de.json +5 -1
- package/src/langs/hfs-lang-el.json +5 -1
- package/src/langs/hfs-lang-en.json +188 -0
- package/src/langs/hfs-lang-es.json +7 -3
- package/src/langs/hfs-lang-fi.json +6 -2
- package/src/langs/hfs-lang-fr.json +10 -6
- package/src/langs/hfs-lang-hu.json +5 -1
- package/src/langs/hfs-lang-it.json +9 -4
- package/src/langs/hfs-lang-ja.json +5 -1
- package/src/langs/hfs-lang-ko.json +5 -1
- package/src/langs/hfs-lang-lt.json +5 -1
- package/src/langs/hfs-lang-ms.json +7 -1
- package/src/langs/hfs-lang-nl.json +5 -1
- package/src/langs/hfs-lang-pl.json +5 -1
- package/src/langs/hfs-lang-pt-br.json +7 -3
- package/src/langs/hfs-lang-ro.json +5 -1
- package/src/langs/hfs-lang-ru.json +7 -3
- package/src/langs/hfs-lang-sr-latn.json +5 -1
- package/src/langs/hfs-lang-sr.json +5 -1
- package/src/langs/hfs-lang-th.json +6 -2
- package/src/langs/hfs-lang-tr.json +6 -2
- package/src/langs/hfs-lang-uk.json +14 -10
- package/src/langs/hfs-lang-vi.json +5 -1
- package/src/langs/hfs-lang-zh-tw.json +5 -1
- package/src/langs/hfs-lang-zh.json +5 -1
- package/src/log.js +7 -1
- package/src/middlewares.js +85 -14
- 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 +12 -0
- package/src/roots.js +22 -13
- package/src/serveFile.js +26 -5
- package/src/serveGuiFiles.js +5 -2
- package/src/update.js +51 -18
- package/src/upload.js +49 -8
- package/src/util-http.js +31 -5
- package/src/vfs.js +84 -4
- package/src/webdav.js +12 -6
- package/admin/assets/AccountsPage-C_ayTmn7.js +0 -3
- package/admin/assets/ArrayField-B20NUH0C.js +0 -2
- package/admin/assets/ConfigFilePage-Dw0-gus3.js +0 -4
- package/admin/assets/CustomHtmlPage-CLGyZfZ8.js +0 -2
- package/admin/assets/DataTable-BwyFEGxv.js +0 -119
- package/admin/assets/DateTimeField-ZUy9IBhM.js +0 -1
- package/admin/assets/EventManager-DomWgRty.js +0 -1
- package/admin/assets/FileField-Bj3P7JUc.js +0 -117
- package/admin/assets/InternetPage-CGMQCcBM.js +0 -8
- package/admin/assets/LangPage-DGFrOItT.js +0 -2
- package/admin/assets/LogsPage-CvCYNhID.js +0 -4
- package/admin/assets/MenuButton-BTRAUH7H.js +0 -1
- package/admin/assets/MonitorPage-CVa2ILH2.js +0 -2
- package/admin/assets/OptionsPage-C3cprG9u.js +0 -3
- package/admin/assets/PluginsPage-CIOnncph.js +0 -1
- package/admin/assets/Tabs-zcHwsK2s.js +0 -1
- package/admin/assets/TextEditor-BeS-mR2I.css +0 -1
- package/admin/assets/TextEditor-CDOjRy40.js +0 -34
- package/admin/assets/VfsPathField-DHifSPJi.js +0 -1
- package/admin/assets/agentIcons-DB1m4pGa.js +0 -1
- package/admin/assets/baseUrl-Bt9R8Da2.js +0 -2
- package/admin/assets/icons-DR_Rvxtn.js +0 -1
- package/admin/assets/index-11n5z0Ez.css +0 -1
- package/admin/assets/index-B3difDBD.js +0 -477
- package/admin/assets/routing-CBrDVeFB.js +0 -1
- package/frontend/assets/index-legacy-tk5ZmBDE.js +0 -1
package/src/update.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.getVersions = getVersions;
|
|
|
10
10
|
exports.getUpdates = getUpdates;
|
|
11
11
|
exports.localUpdateAvailable = localUpdateAvailable;
|
|
12
12
|
exports.previousAvailable = previousAvailable;
|
|
13
|
-
exports.
|
|
13
|
+
exports.updateNotSupported = updateNotSupported;
|
|
14
14
|
exports.update = update;
|
|
15
15
|
const github_1 = require("./github");
|
|
16
16
|
const const_1 = require("./const");
|
|
@@ -29,6 +29,7 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
29
29
|
const argv_1 = require("./argv");
|
|
30
30
|
const promises_2 = require("stream/promises");
|
|
31
31
|
const updateChangelog_1 = require("./updateChangelog");
|
|
32
|
+
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
32
33
|
const updateToBeta = (0, config_1.defineConfig)(misc_1.CFG.update_to_beta, false);
|
|
33
34
|
const autoCheckUpdate = (0, config_1.defineConfig)(misc_1.CFG.auto_check_update, true);
|
|
34
35
|
const lastCheckUpdate = persistence_1.storedMap.singleSync('lastCheckUpdate', 0);
|
|
@@ -59,15 +60,16 @@ exports.checkForUpdates = (0, misc_1.debounceAsync)(async () => {
|
|
|
59
60
|
async function getBestUpdate() {
|
|
60
61
|
return (await getUpdates(true))[0];
|
|
61
62
|
}
|
|
62
|
-
const
|
|
63
|
-
const
|
|
63
|
+
const releaseKeys = ['prerelease', 'tag_name', 'name', 'body', 'assets', 'isNewer', 'versionScalar'];
|
|
64
|
+
const releaseAssetKeys = ['name', 'browser_download_url'];
|
|
65
|
+
const assetPrefix = `hfs-${(0, misc_1.xlate)(process.platform, { win32: 'windows', darwin: 'mac' })}-${process.arch}`;
|
|
64
66
|
const curV = config_1.currentVersion.scalar;
|
|
65
67
|
function prepareRelease(r) {
|
|
66
68
|
const v = (0, config_1.versionToScalar)(r.name);
|
|
67
|
-
return Object.assign(lodash_1.default.pick(r,
|
|
69
|
+
return Object.assign(lodash_1.default.pick(r, releaseKeys), {
|
|
68
70
|
versionScalar: v,
|
|
69
71
|
isNewer: v > curV, // make easy to know what's newer
|
|
70
|
-
assets: r.assets.map((a) => lodash_1.default.pick(a,
|
|
72
|
+
assets: r.assets.map((a) => lodash_1.default.pick(a, releaseAssetKeys))
|
|
71
73
|
});
|
|
72
74
|
}
|
|
73
75
|
async function getVersions(filter, max = 30, stopAtCurrent = false) {
|
|
@@ -91,10 +93,19 @@ async function getVersions(filter, max = 30, stopAtCurrent = false) {
|
|
|
91
93
|
async function getUpdates(strict = false) {
|
|
92
94
|
console.log("Checking for updates");
|
|
93
95
|
void (0, github_1.getProjectInfo)(); // also check for alerts and print them asap in the console
|
|
94
|
-
const stable = prepareRelease(await (0, github_1.getRepoInfo)(const_1.HFS_REPO + '/releases/latest'));
|
|
95
96
|
const includeBetas = updateToBeta.get() || const_1.RUNNING_BETA;
|
|
96
|
-
|
|
97
|
-
const
|
|
97
|
+
const latest = await (0, misc_1.httpStream)(`https://github.com/${const_1.HFS_REPO}/releases/latest`, { method: 'HEAD', noRedirect: true });
|
|
98
|
+
const stableTag = latest.headers.location?.split('/').at(-1);
|
|
99
|
+
if (!stableTag)
|
|
100
|
+
throw Error('Cannot determine latest stable version');
|
|
101
|
+
if ((0, config_1.versionToScalar)(stableTag) === curV && !includeBetas)
|
|
102
|
+
return [];
|
|
103
|
+
let stable = prepareRelease({ prerelease: false, tag_name: stableTag, name: stableTag, body: '', assets: [probableAsset(stableTag)], isNewer: false, versionScalar: 0 });
|
|
104
|
+
if (stable.isNewer || const_1.RUNNING_BETA && !strict)
|
|
105
|
+
stable = await (0, github_1.getRepoInfo)(const_1.HFS_REPO + '/releases/tags/' + stableTag).then(prepareRelease).catch(() => stable);
|
|
106
|
+
const betas = !includeBetas ? [] : await getVersions(x => x.prerelease && x.versionScalar > stable.versionScalar && (!strict || x.isNewer))
|
|
107
|
+
// preserve update detection when GitHub's REST quota is exhausted
|
|
108
|
+
.catch(() => getVersionsFromAtom(x => x.prerelease && x.versionScalar > stable.versionScalar && (!strict || x.isNewer)));
|
|
98
109
|
if (stable.isNewer || const_1.RUNNING_BETA && !strict)
|
|
99
110
|
betas.push(stable);
|
|
100
111
|
if (stable.isNewer && stable.body) {
|
|
@@ -102,7 +113,28 @@ async function getUpdates(strict = false) {
|
|
|
102
113
|
.catch(() => []); // missing history must not prevent installing an available update
|
|
103
114
|
stable.body = (0, updateChangelog_1.updateChangelog)(stable, history, curV);
|
|
104
115
|
}
|
|
105
|
-
return betas;
|
|
116
|
+
return lodash_1.default.sortBy(betas, x => -x.versionScalar);
|
|
117
|
+
async function getVersionsFromAtom(filter) {
|
|
118
|
+
const feed = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false })
|
|
119
|
+
.parse(await (0, misc_1.httpString)(`https://github.com/${const_1.HFS_REPO}/releases.atom`))?.feed;
|
|
120
|
+
return lodash_1.default.sortBy(lodash_1.default.castArray(feed?.entry)
|
|
121
|
+
.map((x) => prepareRelease({
|
|
122
|
+
prerelease: /^v?\d+\.\d+\.\d+-/.test(x.title),
|
|
123
|
+
tag_name: x.id.split('/').at(-1),
|
|
124
|
+
name: x.title,
|
|
125
|
+
body: x.content?.['#text'] || '',
|
|
126
|
+
assets: [probableAsset(x.id.split('/').at(-1))],
|
|
127
|
+
isNewer: false,
|
|
128
|
+
versionScalar: 0,
|
|
129
|
+
}))
|
|
130
|
+
.filter(x => Number.isFinite(x.versionScalar) && !x.name.endsWith('-ignore')
|
|
131
|
+
&& x.versionScalar !== curV && filter(x)), x => -x.versionScalar);
|
|
132
|
+
}
|
|
133
|
+
function probableAsset(tag) {
|
|
134
|
+
const name = `${assetPrefix}-${tag.replace(/^v/, '')}.zip`;
|
|
135
|
+
// release-assets.yml uses this predictable name, keeping degraded checks installable
|
|
136
|
+
return { name, browser_download_url: `https://github.com/${const_1.HFS_REPO}/releases/download/${tag}/${name}` };
|
|
137
|
+
}
|
|
106
138
|
}
|
|
107
139
|
const LOCAL_UPDATE = 'hfs-update.zip'; // update from file takes precedence over net
|
|
108
140
|
const INSTALLED_FN = 'hfs-installed.zip';
|
|
@@ -113,13 +145,16 @@ function localUpdateAvailable() {
|
|
|
113
145
|
function previousAvailable() {
|
|
114
146
|
return (0, misc_1.exists)(PREVIOUS_FN);
|
|
115
147
|
}
|
|
116
|
-
function
|
|
117
|
-
return
|
|
148
|
+
function updateNotSupported() {
|
|
149
|
+
return process.env.DISABLE_UPDATE ? "Automatic updates are disabled"
|
|
150
|
+
: !(argv_1.argv.forceupdate || const_1.IS_BINARY) ? "Only binary versions support automatic updates"
|
|
151
|
+
: (0, promises_1.access)((0, path_1.dirname)(process.execPath), fs_1.constants.W_OK | fs_1.constants.X_OK)
|
|
152
|
+
.then(() => '', () => `Executable directory is not writable: ${(0, path_1.dirname)(process.execPath)}`);
|
|
118
153
|
}
|
|
119
154
|
async function update(tagOrUrl = '') {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
const error = await updateNotSupported();
|
|
156
|
+
if (error)
|
|
157
|
+
throw error;
|
|
123
158
|
let url = tagOrUrl.includes('://') && tagOrUrl;
|
|
124
159
|
if (tagOrUrl === const_1.PREVIOUS_TAG)
|
|
125
160
|
await (0, promises_1.rename)(PREVIOUS_FN, LOCAL_UPDATE);
|
|
@@ -135,11 +170,9 @@ async function update(tagOrUrl = '') {
|
|
|
135
170
|
});
|
|
136
171
|
if (!update)
|
|
137
172
|
throw "No update has been found";
|
|
138
|
-
const
|
|
139
|
-
const assetSearch = `${plat}-${process.arch}`;
|
|
140
|
-
const asset = update.assets.find((x) => x.name.includes(assetSearch) && x.name.endsWith('.zip'));
|
|
173
|
+
const asset = update.assets.find((x) => x.name.includes(assetPrefix) && x.name.endsWith('.zip'));
|
|
141
174
|
if (!asset)
|
|
142
|
-
throw `Asset not found: ${
|
|
175
|
+
throw `Asset not found: ${assetPrefix}`;
|
|
143
176
|
url = asset.browser_download_url;
|
|
144
177
|
}
|
|
145
178
|
if (url) {
|
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: (0, adminApis_1.ctxAdminAccess)(ctx) || undefined,
|
|
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
|
@@ -42,6 +42,7 @@ const walkDir_1 = require("./walkDir");
|
|
|
42
42
|
const node_stream_1 = require("node:stream");
|
|
43
43
|
const adminApis_1 = require("./adminApis");
|
|
44
44
|
const showHiddenFiles = (0, config_1.defineConfig)(misc_1.CFG.show_hidden_files, false);
|
|
45
|
+
const sourceNames = new Map();
|
|
45
46
|
function setVfsPath(node, vfsPath, parent) {
|
|
46
47
|
return (0, misc_1.setHidden)(node, {
|
|
47
48
|
vfsPath: (0, misc_1.enforceFinal)('/', parent?.vfsPath) + (0, misc_1.pathEncode)(vfsPath)
|
|
@@ -189,6 +190,27 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
189
190
|
// does the tree node have a child that goes by this name, otherwise attempt disk
|
|
190
191
|
let virtualName = name;
|
|
191
192
|
let child = parent.children?.find(isSameFilenameAs(name));
|
|
193
|
+
const sameName = isSameFilenameAs(name);
|
|
194
|
+
const sourceName = Object.entries(parent.rename || {}).find(([, to]) => sameName(to))?.[0] || name;
|
|
195
|
+
if (!child) {
|
|
196
|
+
child = findSourceChild();
|
|
197
|
+
if (!child && const_1.IS_WINDOWS && parent.source) {
|
|
198
|
+
const aliases = await windowsPathNames((0, path_1.join)(parent.source, sourceName), parent.source);
|
|
199
|
+
if (!aliases)
|
|
200
|
+
return;
|
|
201
|
+
child = findSourceChild(aliases);
|
|
202
|
+
if (!child) {
|
|
203
|
+
const normalized = new Set(aliases.map(normalizeFilename));
|
|
204
|
+
const candidates = parent.children?.filter(candidate => candidate.source
|
|
205
|
+
&& normalized.has(normalizeFilename((0, path_1.basename)(candidate.source)))) || [];
|
|
206
|
+
if (candidates.length > 1)
|
|
207
|
+
return; // an unverified physical alias must identify one VFS child
|
|
208
|
+
const [candidate] = candidates;
|
|
209
|
+
if (candidate?.source && await isSameFilePath((0, path_1.dirname)(candidate.source), parent.source))
|
|
210
|
+
child = candidate;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
192
214
|
if (child) { // found as vfs node
|
|
193
215
|
virtualName = getNodeName(child);
|
|
194
216
|
await setIsFolder(child); // in case it's pointing to a folder that didn't exist at loading time
|
|
@@ -202,7 +224,6 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
202
224
|
const ret = {};
|
|
203
225
|
let onDisk = name;
|
|
204
226
|
if (parent.rename) { // reverse the mapping
|
|
205
|
-
const sameName = isSameFilenameAs(name);
|
|
206
227
|
const entries = Object.entries(parent.rename);
|
|
207
228
|
// search display names first: with { A: B, C: A }, A must resolve to C rather than be hidden
|
|
208
229
|
const asDisplayName = entries.find(([, to]) => sameName(to));
|
|
@@ -211,12 +232,17 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
211
232
|
virtualName = asDisplayName[1];
|
|
212
233
|
}
|
|
213
234
|
else {
|
|
214
|
-
|
|
235
|
+
if (!await canonicalizeWindowsAlias())
|
|
236
|
+
return;
|
|
237
|
+
const sameSourceName = isSameFilenameAs(onDisk);
|
|
238
|
+
const asPhysicalName = entries.find(([from]) => sameSourceName(from));
|
|
215
239
|
if (asPhysicalName)
|
|
216
240
|
return; // a VFS rename replaces the original public name
|
|
217
241
|
}
|
|
218
242
|
ret.rename = renameUnderPath(parent.rename, onDisk);
|
|
219
243
|
}
|
|
244
|
+
else if (!await canonicalizeWindowsAlias())
|
|
245
|
+
return;
|
|
220
246
|
if (!(0, misc_1.isValidFileName)(onDisk))
|
|
221
247
|
return;
|
|
222
248
|
ret.source = (0, path_1.join)(parent.source, onDisk);
|
|
@@ -225,6 +251,22 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
225
251
|
if (assumeMissingToBeFolder)
|
|
226
252
|
ret.isFolder ??= true;
|
|
227
253
|
return ret;
|
|
254
|
+
async function canonicalizeWindowsAlias() {
|
|
255
|
+
if (!const_1.IS_WINDOWS || !name.includes('~'))
|
|
256
|
+
return true;
|
|
257
|
+
// Windows exposes 8.3 aliases that must keep the long name's VFS identity
|
|
258
|
+
const longPath = await convertWindowsPath((0, path_1.join)(parent.source, name), true);
|
|
259
|
+
if (!longPath)
|
|
260
|
+
return true; // new names containing '~' are still valid
|
|
261
|
+
if (!await isSameFilePath((0, path_1.dirname)(longPath), parent.source))
|
|
262
|
+
return false;
|
|
263
|
+
onDisk = virtualName = (0, path_1.basename)(longPath);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function findSourceChild(names = [sourceName]) {
|
|
268
|
+
const normalized = new Set(names.map(normalizeFilename));
|
|
269
|
+
return parent.children?.find(x => getSourceNames(x, parent).some(source => normalized.has(normalizeFilename(source))));
|
|
228
270
|
}
|
|
229
271
|
}
|
|
230
272
|
const smartUncFolderDetection = (0, config_1.defineConfig)(misc_1.CFG.smart_unc_folder_detection, false);
|
|
@@ -261,8 +303,42 @@ async function reviewVfs() {
|
|
|
261
303
|
usedNames.add(normalizeFilename(getNodeName(child)));
|
|
262
304
|
}
|
|
263
305
|
await Promise.allSettled(node.children.map(recur));
|
|
306
|
+
await Promise.allSettled(node.children.map(child => setSourceNames(child, node)));
|
|
264
307
|
})(exports.vfs.compiled());
|
|
265
308
|
}
|
|
309
|
+
async function setSourceNames(node, parent) {
|
|
310
|
+
if (!node.source || !parent.source)
|
|
311
|
+
return;
|
|
312
|
+
const sameParent = normalizeFilename((0, path_1.dirname)((0, path_1.resolve)(node.source))) === normalizeFilename((0, path_1.resolve)(parent.source));
|
|
313
|
+
if (!sameParent && (!const_1.IS_WINDOWS || !await isSameFilePath((0, path_1.dirname)(node.source), parent.source)))
|
|
314
|
+
return;
|
|
315
|
+
const names = [(0, path_1.basename)(node.source)];
|
|
316
|
+
const key = sourceNamesKey(node, parent);
|
|
317
|
+
if (const_1.IS_WINDOWS) {
|
|
318
|
+
const converted = await Promise.all([convertWindowsPath(node.source), convertWindowsPath(node.source, true)]);
|
|
319
|
+
names.push(...converted.map(x => x && (0, path_1.basename)(x)).filter(Boolean));
|
|
320
|
+
}
|
|
321
|
+
sourceNames.set(key, lodash_1.default.uniq(names));
|
|
322
|
+
}
|
|
323
|
+
function convertWindowsPath(path, isLong = false) {
|
|
324
|
+
return new Promise(resolvePromise => {
|
|
325
|
+
if (!fswin_1.default.convertPath(path, resolvePromise, isLong))
|
|
326
|
+
resolvePromise(undefined);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
async function windowsPathNames(path, parent) {
|
|
330
|
+
const converted = await Promise.all([convertWindowsPath(path), convertWindowsPath(path, true)]);
|
|
331
|
+
const existing = converted.find(Boolean);
|
|
332
|
+
if (existing && !await isSameFilePath((0, path_1.dirname)(existing), parent))
|
|
333
|
+
return;
|
|
334
|
+
return lodash_1.default.uniq([(0, path_1.basename)(path), ...converted.map(x => x && (0, path_1.basename)(x)).filter(Boolean)]);
|
|
335
|
+
}
|
|
336
|
+
function getSourceNames(node, parent) {
|
|
337
|
+
return node.source && parent.source ? sourceNames.get(sourceNamesKey(node, parent)) || [] : [];
|
|
338
|
+
}
|
|
339
|
+
function sourceNamesKey(node, parent) {
|
|
340
|
+
return normalizeFilename((0, path_1.resolve)(parent.source)) + '\0' + normalizeFilename((0, path_1.resolve)(node.source));
|
|
341
|
+
}
|
|
266
342
|
exports.saveVfs = (0, misc_1.debounceAsync)(async () => {
|
|
267
343
|
await reviewVfs(); // refresh runtime-derived folder flags before saving mutated VFS state
|
|
268
344
|
exports.vfs.set(exports.vfs.compiled()); // sync the mutable runtime tree back to config state before persisting it
|
|
@@ -385,6 +461,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
385
461
|
started = true;
|
|
386
462
|
const { source } = parent;
|
|
387
463
|
const taken = new Set();
|
|
464
|
+
const takenSources = new Set();
|
|
388
465
|
const maskApplier = parentMaskApplier(parent);
|
|
389
466
|
const visitLater = [];
|
|
390
467
|
const childrenWorking = parent.children?.length && Promise.all(parent.children.map(async (child) => {
|
|
@@ -392,7 +469,9 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
392
469
|
return;
|
|
393
470
|
const nodeName = getNodeName(child);
|
|
394
471
|
const name = prefixPath + nodeName;
|
|
395
|
-
taken
|
|
472
|
+
taken.add(normalizeFilename(name));
|
|
473
|
+
for (const sourceName of getSourceNames(child, parent))
|
|
474
|
+
takenSources.add(normalizeFilename(prefixPath + sourceName));
|
|
396
475
|
const item = setVfsPath({ ...child, original: child, name, parent }, name, parent);
|
|
397
476
|
if (await cantSee(item))
|
|
398
477
|
return;
|
|
@@ -434,7 +513,8 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
434
513
|
renamed = dir + '/' + renamed;
|
|
435
514
|
}
|
|
436
515
|
const name = prefixPath + (renamed || path);
|
|
437
|
-
if (taken
|
|
516
|
+
if (taken.has(normalizeFilename(name))
|
|
517
|
+
|| takenSources.has(normalizeFilename(prefixPath + path))) // taken by vfs node above
|
|
438
518
|
return false; // false just in case it's a folder
|
|
439
519
|
const item = setVfsPath({ name, isFolder, source: (0, path_1.join)(source, path), parent, stats: entry.stats }, name, parent);
|
|
440
520
|
// masks containing '/' must be matched against the relative path while keeping walkDir recursion enabled
|
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");
|
|
@@ -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,3 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-CacB07dV.js";import{t}from"./DateTimeField-ZUy9IBhM.js";import{n,r,t as i}from"./MenuButton-BTRAUH7H.js";import{n as a,t as o}from"./VfsPathField-DHifSPJi.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 E,St as D,Ta as O,U as k,Ut as A,W as j,Wr as M,Yt as N,_ as ee,_i as P,_r as te,ai as F,en as I,fr as L,ft as R,gt as ne,ht as re,it as ie,j as ae,jr as oe,ka as se,lr as z,lt as ce,mt as B,ni as le,nr as ue,q as de,ri as fe,st as pe,tr as me,tt as V,vi as H,w as he,y as U,zt as ge}from"./index-B3difDBD.js";import{$ as _e,J as ve,Q as ye,X as be,Y as xe,Z as Se,et as W,it as Ce,nt as we,rt as G,tt as Te}from"./icons-DR_Rvxtn.js";var K=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}=ie(),[_,v]=(0,K.useState)(e),[x,S]=(0,K.useState)([]),C=I();(0,K.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,K.useRef)(),{members:A}=e,j=(u=e.plugin)==null?void 0:u.auth;return(0,K.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,K.createElement)(c,f(f({icon:G,title:`Delete`,confirm:`Delete ${e.username}?`},h===e.username&&{disabled:!0,title:`Cannot delete current account`}),{},{onClick:()=>z(`del_account`,{username:e.username}).then(a)})),(0,K.createElement)(c,{icon:xe,title:`Invalidate past sessions${s(`
|
|
2
|
-
(already invalidated sessions before `,D(e.invalidated||0),`)`)}`,confirm:`Invalidate all sessions up to now for "${e.username}"?`,doneMessage:!0,onClick:()=>z(`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&&z(`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,K.createElement)(H,{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:R,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,K.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:R,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:R,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:R,xs:12,sm:6,lg:4,helperText:`At next login, but can be dismissed`},{k:`disable_password_change`,label:`Password change`,comp:ne,xs:12,sm:6,lg:4,defaultValue:null,options:{[`Default (${_.canChangePassword?`Allowed`:`Disabled`})`]:null,Allowed:!1,Disabled:!0}},A?T&&!A.length?(0,K.createElement)(F,{},`No members`):A.length>0&&(0,K.createElement)(E,{gap:0,flexWrap:`wrap`},`${A.length} members: `,N(`,\xA0`,(m=e.members)==null?void 0:m.map(e=>(0,K.createElement)(r.includes(e)?`i`:`span`,{},e))),(0,K.createElement)(U,{icon:G,confirm:`Delete ${e.members.length} accounts?`,onClick:()=>z(`del_account`,{username:e.members}).then(a),sx:{verticalAlign:`text-top`}})):null,T&&(0,K.createElement)(H,{severity:`info`},`To add users to this group, select the user and then click "Inherit"`),{k:`belongs`,comp:re,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`},{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:B,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 z(`add_account`,u);if(r)try{yield p(_.username,r)}catch(e){throw z(`del_account`,{username:_.username}),e}n(e==null?void 0:e.username,d);return}let m=yield z(`set_account`,{username:e.username,changes:u});r&&(yield p(_.username,r),v(e=>f(f({},e),{},{password:``,password2:``}))),e.username===h&&(V.username=m.username),n(m==null?void 0:m.username,d)})()}})})}var q=e(L());se(),u();var J={fieldSeparator:`,`,lineSeparator:`
|
|
3
|
-
`,quote:`"`},Y={field:0,fieldOffset:0,line:0,lineOffset:0,quoted:!1,appendCell:!1,appendField:!1,appendRow:!1},X=class extends Error{constructor(e,t){super(`Invalid CSV at ${e}:${t}`)}},Oe=class{constructor(e=J){C(this,`_rows`,[]),C(this,`_row`,[]),C(this,`_cell`,``),C(this,`_options`,J),C(this,`_state`,f({},Y)),C(this,`_index`,0),C(this,`_current`,``),C(this,`_previous`,``),C(this,`_quoteState`,f({},Y)),this._options=Object.assign({},J,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 X(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 X(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({},Y),this._index=0,this._current=``,this._previous=``,this._quoteState=f({},Y)}};u();function ke(e){return Z.apply(this,arguments)}function Z(){return Z=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:B,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,K.createElement)(ve),children:`Go`},fields:[(0,K.createElement)(F,{sx:{p:1}},`Total lines:`,i.length),{k:`skipFirstLines`,comp:B,max:i.length-1,typing:!0,md:6,getError:e=>e!=null&&!Number.isInteger(e)&&`Enter an integer`,helperText:(0,K.createElement)(K.Fragment,{},`First line: `,(0,K.createElement)(`code`,{},t.join(`, `)))},{k:`overwriteExistingAccounts`,comp:R,md:6},f(f({k:`usernameColumn`},a),{},{helperText:(0,K.createElement)(K.Fragment,{},`First username: `,n.u)}),f(f({k:`passwordColumn`},a),{},{helperText:(0,K.createElement)(K.Fragment,{},`First password: `,n.p)}),f(f({k:`groupColumn`},a),{},{helperText:(0,K.createElement)(K.Fragment,{},`First group: `,n.g)}),f(f({k:`redirectColumn`},a),{},{helperText:(0,K.createElement)(K.Fragment,{},`First redirect: `,n.r)})]}}});if(!c)return;let{close:l}=A({title:`Importing...`,Content(){let[t,n]=(0,K.useState)(0),[r,a]=(0,K.useState)();return(0,K.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 z(`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,K.createElement)(F,{sx:{display:`flex`,gap:2,alignItems:`center`}},(0,K.createElement)(he,{icon:W,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`})}),Z.apply(this,arguments)}u();var Ae=` `,Q=e=>e==null?void 0:e.split(Ae).at(-1);function je(){let{username:e,accountsAsTree:t}=ie(),{data:o,reload:s,element:l}=ee(`get_accounts`),[u,d]=(0,K.useState)([]),p=Array.isArray(u);(0,K.useEffect)(()=>{Array.isArray(o==null?void 0:o.list)&&p&&d(u.filter(e=>o.list.find(t=>(t==null?void 0:t.username)===Q(e))))},[o]);let h=(0,K.useMemo)(()=>o&&q.default.sortBy(o.list,[e=>!e.isGroup,e=>!e.adminActualAccess,`username`]),[o]),v=p&&q.default.find(h,{username:Q(u[0])}),y=m(`md`),C=!(u.length>0)||!h?null:p&&u.length>1?(0,K.createElement)(K.Fragment,{},(0,K.createElement)(E,{},(0,K.createElement)(P,{variant:`h6`},u.length+` selected`),(0,K.createElement)(U,{onClick:I,icon:G},`Remove`)),(0,K.createElement)(_,{},q.default.uniq(u.map(Q)).map(e=>(0,K.createElement)(a,{key:e},(0,K.createElement)(oe,{},e))))):ue(v||j(),e=>(0,K.createElement)(De,{account:e,groups:h.filter(e=>e.isGroup).map(e=>e.username),addToBar:y&&[(0,K.createElement)(F,{sx:{flex:1}}),$(e,{fontSize:`large`,sx:{p:1}}),(0,K.createElement)(c,{icon:pe,title:`Close`,onClick:N})],reload:s,done(e,t){d(y?[e]:[]),s(),S(``,t)}}));(0,K.useEffect)(()=>{if(y||!C||!u.length)return;let{close:e}=A({title:q.default.isString(u)?q.default.startCase(u):u.length>1?`Multiple selection`:v?(v.isGroup?`Group: `:`User: `)+v.username:`?`,Content:()=>C,onClose(e){e||N()}});return()=>void e(!0)},[y,u,v]);let w={height:`100%`,display:`flex`,flexDirection:`column`,overflow:`auto`},[D,O]=T(`Show tree`,`Show list`,()=>({icon:te}),t);return V.accountsAsTree=D,l||(0,K.createElement)(M,{container:!0,sx:{rowSpacing:1,columnSpacing:2,top:0,flex:`1 1 auto`,height:0}},(0,K.createElement)(M,{size:{xs:12,md:5,lg:4,xl:5},sx:w},(0,K.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,K.createElement)(i,{variant:`contained`,startIcon:(0,K.createElement)(Se),items:[{children:`user`,onClick:()=>d(`new-user`)},{children:`group`,onClick:()=>d(`new-group`)},{children:`from CSV`,onClick:()=>ke(s)}]},`Add`),x(s),O,(h==null?void 0:h.length)>0&&(0,K.createElement)(P,{sx:{p:1}},`${h.length} account(s)`)),!(h!=null&&h.length)&&(0,K.createElement)(H,{severity:`info`},ge`To access administration <u>remotely</u> you will need to create a user account with admin permission`),(0,K.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,K.createElement)(n,{key:t.username,itemId:r+t.username,label:(0,K.createElement)(F,{sx:{display:`flex`,flexWrap:`wrap`,padding:`.2em 0`,columnGap:`.5em`,alignItems:`center`}},$(t),(t.disabled||t.canLogin===!1)&&ae(we,t.disabled?`Disabled`:`Disabled by its groups`,t.disabled?void 0:{color:`text.secondary`}),(t.expire||t.days_to_live)&&(0,K.createElement)(be),t.adminActualAccess&&ae(_e,`Can login into Admin`),t.username,!!((i=t.belongs)!=null&&i.length)&&(0,K.createElement)(F,{sx:{color:`text.secondary`,fontSize:`small`}},`(`,(a=t.belongs)==null?void 0:a.join(`, `),`)`))},D&&e(h.filter(e=>{var n;return(n=t.directMembers)==null?void 0:n.includes(e.username)}),r+t.username+Ae))})})(D?h.filter(e=>!h.some(t=>{var n;return(n=t.members)==null?void 0:n.includes(e.username)})):h))),y&&C&&(0,K.createElement)(M,{size:`grow`,sx:f(f({},w),{},{maxWidth:`100%`})},(0,K.createElement)(fe,{sx:{overflow:`initial`}},(0,K.createElement)(le,{},C))));function j(){return{username:``,hasPassword:u===`new-user`,adminActualAccess:!1,invalidated:void 0,canLogin:!0,canChangePassword:!0,isGroup:u===`new-group`,members:[],directMembers:[]}}function N(){d([])}function I(){return L.apply(this,arguments)}function L(){return L=b(function*(){if(typeof u==`string`||u.some(t=>Q(t)===e)&&!(yield k(`You cannot ask to delete the account you are using. Continue with the rest?`)))return;let t=q.default.without(q.default.uniq(u.map(Q)),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 z(`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`)}),L.apply(this,arguments)}}function $(e,t={}){return(0,K.createElement)(e.isGroup?W: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-BwyFEGxv.js";import{t as r}from"./DateTimeField-ZUy9IBhM.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-B3difDBD.js";import{F as ne,I as re,L as ie,P as ae,q as oe,rt as se}from"./icons-DR_Rvxtn.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-B3difDBD.js";import{K as v,q as y}from"./icons-DR_Rvxtn.js";import{t as b}from"./TextEditor-CDOjRy40.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-B3difDBD.js";import{t as D}from"./TextEditor-CDOjRy40.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};
|