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/adminApis.js
CHANGED
|
@@ -149,11 +149,11 @@ exports.adminApis = {
|
|
|
149
149
|
configFile: config_1.configFile.getPath(),
|
|
150
150
|
urls: await (0, listen_1.getUrls)(),
|
|
151
151
|
ips: await (0, listen_1.getIps)(false),
|
|
152
|
-
connectionAddress: (0,
|
|
152
|
+
connectionAddress: (0, misc_1.normalizeIp)(ctx.socket.localAddress || ''),
|
|
153
153
|
baseUrl: await (0, listen_1.getBaseUrlOrDefault)(),
|
|
154
154
|
roots: roots_1.roots.get(),
|
|
155
155
|
anyAccountCanLoginAdmin: anyAccountCanLoginAdmin(),
|
|
156
|
-
updatePossible: !await (0, update_1.
|
|
156
|
+
updatePossible: !await (0, update_1.updateNotSupported)() && (await (0, update_1.localUpdateAvailable)() ? 'local' : true),
|
|
157
157
|
previousVersionAvailable: await (0, update_1.previousAvailable)(),
|
|
158
158
|
autoCheckUpdateResult: update_1.autoCheckUpdateResult.get(), // in this form, we get the same type of the serialized json
|
|
159
159
|
alerts: // in this form, we get the same type of the serialized json
|
package/src/adminLang.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2026, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.adminLang = void 0;
|
|
8
|
+
exports.getAdminLangs = getAdminLangs;
|
|
9
|
+
exports.getAdminLangData = getAdminLangData;
|
|
10
|
+
exports.invalidateAdminLang = invalidateAdminLang;
|
|
11
|
+
const promises_1 = require("fs/promises");
|
|
12
|
+
const misc_1 = require("./misc");
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const expiringCache_1 = require("./expiringCache");
|
|
15
|
+
const embedded_1 = __importDefault(require("./admin-langs/embedded"));
|
|
16
|
+
const const_1 = require("./const");
|
|
17
|
+
const lang_1 = require("./lang");
|
|
18
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
19
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
20
|
+
async function getAdminLangs() {
|
|
21
|
+
const files = await (0, fast_glob_1.default)((0, lang_1.code2file)('*', true));
|
|
22
|
+
return lodash_1.default.uniq([...Object.keys(embedded_1.default), ...files.map(name => (0, lang_1.file2code)(name, true))
|
|
23
|
+
.filter(code => (0, lang_1.normalizeLangCode)(code) === code)]);
|
|
24
|
+
}
|
|
25
|
+
exports.adminLang = (0, config_1.defineConfig)(misc_1.CFG.admin_lang, '', lang_1.normalizeLangCode);
|
|
26
|
+
const cache = (0, expiringCache_1.expiringCache)(3_000);
|
|
27
|
+
function getAdminLangData(ctx, langs) {
|
|
28
|
+
const configured = exports.adminLang.compiled();
|
|
29
|
+
const code = langs.includes(configured) ? configured : browserAdminLang(ctx, langs);
|
|
30
|
+
return cache.try(code, async () => ({
|
|
31
|
+
[code]: (0, misc_1.tryJson)(await (0, promises_1.readFile)((0, lang_1.code2file)(code, true), 'utf8').catch(() => ''))
|
|
32
|
+
|| embedded_1.default[code] || embedded_1.default.en,
|
|
33
|
+
...code === const_1.EMBEDDED_LANGUAGE ? {} : { [const_1.EMBEDDED_LANGUAGE]: embedded_1.default.en },
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
function invalidateAdminLang(code) {
|
|
37
|
+
cache.delete(code); // uploads and deletions must be visible immediately after the Admin reloads
|
|
38
|
+
}
|
|
39
|
+
function browserAdminLang(ctx, langs) {
|
|
40
|
+
const accepted = ctx?.get('Accept-Language') || '';
|
|
41
|
+
for (const raw of accepted.split(',')) {
|
|
42
|
+
const code = (0, lang_1.normalizeLangCode)(raw);
|
|
43
|
+
if (langs.includes(code))
|
|
44
|
+
return code;
|
|
45
|
+
const base = code.split('-')[0] || '';
|
|
46
|
+
if (langs.includes(base))
|
|
47
|
+
return base;
|
|
48
|
+
}
|
|
49
|
+
return const_1.EMBEDDED_LANGUAGE;
|
|
50
|
+
}
|
package/src/api.lang.js
CHANGED
|
@@ -12,30 +12,36 @@ const const_1 = require("./const");
|
|
|
12
12
|
const misc_1 = require("./misc");
|
|
13
13
|
const lang_1 = require("./lang");
|
|
14
14
|
const embedded_1 = __importDefault(require("./langs/embedded"));
|
|
15
|
+
const embedded_2 = __importDefault(require("./admin-langs/embedded"));
|
|
16
|
+
const adminLang_1 = require("./adminLang");
|
|
15
17
|
const SendList_1 = require("./SendList");
|
|
16
18
|
const apis = {
|
|
17
19
|
get_langs() {
|
|
18
20
|
return new SendList_1.SendListReadable({
|
|
19
21
|
doAtStart: async (list) => {
|
|
20
|
-
for
|
|
21
|
-
name
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
for (const admin of [false, true]) {
|
|
23
|
+
for await (const name of fast_glob_1.default.stream((0, lang_1.code2file)('*', admin))) {
|
|
24
|
+
const code = (0, lang_1.file2code)(String(name), admin);
|
|
25
|
+
try {
|
|
26
|
+
const data = JSON.parse(await (0, promises_1.readFile)(String(name), 'utf8'));
|
|
27
|
+
list.add({ ...lodash_1.default.omit(data, 'translate'), code, admin, embedded: false });
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
26
30
|
}
|
|
27
|
-
|
|
31
|
+
for (const [code, data] of Object.entries(admin ? embedded_2.default : embedded_1.default))
|
|
32
|
+
list.add({ ...lodash_1.default.omit(data, 'translate'), code, admin, embedded: true });
|
|
28
33
|
}
|
|
29
|
-
for (const [code, data] of Object.entries(embedded_1.default))
|
|
30
|
-
list.add({ code, embedded: true, ...lodash_1.default.omit(data, 'translate') });
|
|
31
34
|
list.close();
|
|
32
35
|
}
|
|
33
36
|
});
|
|
34
37
|
},
|
|
35
|
-
async del_lang({ code }) {
|
|
38
|
+
async del_lang({ code, admin = false }) {
|
|
39
|
+
(0, misc_1.apiAssertTypes)({ string: { code }, boolean: { admin } });
|
|
36
40
|
validateCode(code);
|
|
37
41
|
try {
|
|
38
|
-
await (0, promises_1.rm)((0, lang_1.code2file)(code));
|
|
42
|
+
await (0, promises_1.rm)((0, lang_1.code2file)(code, admin));
|
|
43
|
+
if (admin)
|
|
44
|
+
(0, adminLang_1.invalidateAdminLang)(code.toLowerCase());
|
|
39
45
|
return {};
|
|
40
46
|
}
|
|
41
47
|
catch (e) {
|
|
@@ -45,14 +51,17 @@ const apis = {
|
|
|
45
51
|
async add_langs({ langs }) {
|
|
46
52
|
(0, misc_1.apiAssertTypes)({ object: { langs } });
|
|
47
53
|
for (let [code, content] of Object.entries(langs)) {
|
|
48
|
-
|
|
54
|
+
const admin = code.startsWith('hfs-admin-lang-');
|
|
55
|
+
code = (0, lang_1.file2code)(code, admin);
|
|
49
56
|
validateCode(code);
|
|
50
|
-
const fn = (0, lang_1.code2file)(code);
|
|
57
|
+
const fn = (0, lang_1.code2file)(code, admin);
|
|
51
58
|
const s = String(content);
|
|
52
59
|
const o = (0, misc_1.tryJson)(s);
|
|
53
60
|
if (!o?.translate)
|
|
54
61
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, "bad content for file " + fn);
|
|
55
62
|
await (0, promises_1.writeFile)(fn, s, 'utf8');
|
|
63
|
+
if (admin)
|
|
64
|
+
(0, adminLang_1.invalidateAdminLang)(code.toLowerCase());
|
|
56
65
|
}
|
|
57
66
|
return {};
|
|
58
67
|
}
|
package/src/api.log.js
CHANGED
|
@@ -33,25 +33,27 @@ exports.default = {
|
|
|
33
33
|
await (0, serveFile_1.serveFile)(ctx, log.path);
|
|
34
34
|
return null;
|
|
35
35
|
},
|
|
36
|
-
get_log({ file = 'log' }, ctx) {
|
|
36
|
+
get_log({ file = 'log', skipInitial }, ctx) {
|
|
37
37
|
(0, misc_1.apiAssertTypes)({ string: { file } });
|
|
38
38
|
const files = file.split('|'); // potentially more than one
|
|
39
39
|
return new SendList_1.SendListReadable({
|
|
40
40
|
bufferTime: 10,
|
|
41
41
|
async doAtStart(list) {
|
|
42
42
|
if (file === 'disconnections') {
|
|
43
|
-
for (const x of connections_1.disconnectionsLog)
|
|
43
|
+
for (const x of skipInitial ? [] : connections_1.disconnectionsLog)
|
|
44
44
|
list.add(x);
|
|
45
45
|
ctx.res.once('close', events_1.default.on('disconnection', x => list.add(x)));
|
|
46
46
|
return list.ready();
|
|
47
47
|
}
|
|
48
48
|
if (file === 'ips') {
|
|
49
|
+
if (skipInitial)
|
|
50
|
+
return list.ready();
|
|
49
51
|
for await (const [k, v] of ips_1.ips.iterator())
|
|
50
52
|
list.add({ ip: k, ...v });
|
|
51
53
|
return list.ready();
|
|
52
54
|
}
|
|
53
55
|
if (file === 'console') {
|
|
54
|
-
for (const chunk of lodash_1.default.chunk(consoleLog_1.consoleLog, 1000)) { // avoid occupying the thread too long
|
|
56
|
+
for (const chunk of lodash_1.default.chunk(skipInitial ? [] : consoleLog_1.consoleLog, 1000)) { // avoid occupying the thread too long
|
|
55
57
|
for (const x of chunk)
|
|
56
58
|
list.add(x);
|
|
57
59
|
await (0, cross_1.wait)(0);
|
package/src/api.net.js
CHANGED
|
@@ -14,6 +14,7 @@ const promises_1 = require("dns/promises");
|
|
|
14
14
|
const net_1 = require("net");
|
|
15
15
|
const nat_1 = require("./nat");
|
|
16
16
|
const acme_1 = require("./acme");
|
|
17
|
+
const acmeConfig_1 = require("./acmeConfig");
|
|
17
18
|
const selfCheck_1 = require("./selfCheck");
|
|
18
19
|
exports.default = {
|
|
19
20
|
get_nat: nat_1.getNatInfo,
|
|
@@ -83,13 +84,29 @@ exports.default = {
|
|
|
83
84
|
const results = (0, misc_1.onlyTruthy)(await (0, misc_1.promiseBestEffort)(publicIps.map(ip => (0, selfCheck_1.selfCheck)(`${proto}://${(0, misc_1.ipForUrl)(ip)}${finalPort === defPort ? '' : ':' + finalPort}`))));
|
|
84
85
|
return results.length ? results : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
|
|
85
86
|
},
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
get_acme: acmeConfig_1.getAcmeSettings,
|
|
88
|
+
get_acme_status: acme_1.getAcmeStatusEvents,
|
|
89
|
+
async set_acme({ settings }) {
|
|
90
|
+
if ((0, acme_1.getAcmeStatus)().state === 'running')
|
|
91
|
+
throw new apiMiddleware_1.ApiError(409, "Certificate request already running");
|
|
92
|
+
if (!settings || typeof settings !== 'object')
|
|
93
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
|
|
94
|
+
return (0, acmeConfig_1.setAcmeSettings)(settings).catch(error => { throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, error.message); });
|
|
95
|
+
},
|
|
96
|
+
async make_cert({ domain, email, altNames, background }) {
|
|
97
|
+
(0, misc_1.apiAssertTypes)({ string: { domain }, string_undefined: { email }, array_undefined: { altNames }, boolean_undefined: { background } });
|
|
88
98
|
if (!domain)
|
|
89
99
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad params');
|
|
90
100
|
if (altNames?.some((name) => typeof name !== 'string'))
|
|
91
101
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad altNames');
|
|
92
|
-
|
|
102
|
+
if ((0, acme_1.getAcmeStatus)().state === 'running')
|
|
103
|
+
throw new apiMiddleware_1.ApiError(409, "Certificate request already running");
|
|
104
|
+
const pending = (0, acme_1.makeCert)(domain, email, altNames);
|
|
105
|
+
if (background) {
|
|
106
|
+
void pending.catch(() => { }); // the status stream carries the error even after the browser disconnects
|
|
107
|
+
return { id: (0, acme_1.getAcmeStatus)().id };
|
|
108
|
+
}
|
|
109
|
+
await pending.catch(e => {
|
|
93
110
|
throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e.message || String(e));
|
|
94
111
|
});
|
|
95
112
|
return {};
|
package/src/api.plugins.js
CHANGED
|
@@ -13,9 +13,10 @@ const promises_1 = require("fs/promises");
|
|
|
13
13
|
const github_1 = require("./github");
|
|
14
14
|
const const_1 = require("./const");
|
|
15
15
|
const SendList_1 = require("./SendList");
|
|
16
|
+
const pluginCatalog_1 = require("./pluginCatalog");
|
|
16
17
|
const apis = {
|
|
17
|
-
get_plugins({}, ctx) {
|
|
18
|
-
const list = new SendList_1.SendListReadable({ addAtStart: [...(0, plugins_1.mapPlugins)(serialize, false), ...(0, plugins_1.getInactivePlugins)().map(serialize)] });
|
|
18
|
+
get_plugins({ skipInitial }, ctx) {
|
|
19
|
+
const list = new SendList_1.SendListReadable({ addAtStart: skipInitial ? [] : [...(0, plugins_1.mapPlugins)(serialize, false), ...(0, plugins_1.getInactivePlugins)().map(serialize)] });
|
|
19
20
|
return list.events(ctx, {
|
|
20
21
|
pluginInstalled: p => list.add(serialize(p)),
|
|
21
22
|
'pluginStarted pluginStopped pluginUpdated': p => {
|
|
@@ -26,7 +27,7 @@ const apis = {
|
|
|
26
27
|
pluginLog: id => list.update({ id }, { log: true }) // SendList is already capping frequency
|
|
27
28
|
});
|
|
28
29
|
},
|
|
29
|
-
async get_plugin_updates({}, ctx) {
|
|
30
|
+
async get_plugin_updates({ skipInitial }, ctx) {
|
|
30
31
|
return new SendList_1.SendListReadable({
|
|
31
32
|
async doAtStart(list) {
|
|
32
33
|
const errs = {};
|
|
@@ -38,6 +39,8 @@ const apis = {
|
|
|
38
39
|
list.update({ id }, { updated: true });
|
|
39
40
|
}
|
|
40
41
|
});
|
|
42
|
+
if (skipInitial)
|
|
43
|
+
return list.ready();
|
|
41
44
|
await plugins_1.pluginsScanned;
|
|
42
45
|
await Promise.allSettled(lodash_1.default.map((0, github_1.getFolder2repo)(), async (repo, folder) => {
|
|
43
46
|
try {
|
|
@@ -73,11 +76,17 @@ const apis = {
|
|
|
73
76
|
async start_plugin({ id }) {
|
|
74
77
|
assertPluginId(id);
|
|
75
78
|
if ((0, plugins_1.isPluginRunning)(id))
|
|
76
|
-
return
|
|
79
|
+
return serializeRunningPlugin(id);
|
|
77
80
|
if (plugins_1.suspendPlugins.get())
|
|
78
81
|
return new apiMiddleware_1.ApiError(misc_1.HTTP_PRECONDITION_FAILED, 'all plugins suspended');
|
|
79
82
|
await (0, plugins_1.stopPlugin)(id);
|
|
80
|
-
|
|
83
|
+
try {
|
|
84
|
+
await (0, plugins_1.startPlugin)(id);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e.message);
|
|
88
|
+
}
|
|
89
|
+
return serializeRunningPlugin(id);
|
|
81
90
|
},
|
|
82
91
|
async stop_plugin({ id }) {
|
|
83
92
|
assertPluginId(id);
|
|
@@ -104,42 +113,63 @@ const apis = {
|
|
|
104
113
|
}
|
|
105
114
|
};
|
|
106
115
|
},
|
|
107
|
-
get_online_plugins({ text }, ctx) {
|
|
116
|
+
get_online_plugins({ text, skipInitial }, ctx) {
|
|
108
117
|
if (text !== undefined && !lodash_1.default.isString(text))
|
|
109
118
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad text');
|
|
110
119
|
return new SendList_1.SendListReadable({
|
|
111
120
|
async doAtStart(list) {
|
|
121
|
+
// resumed clients retain their own rows; updates for other repos are ignored by useApiList
|
|
112
122
|
const repos = [];
|
|
113
123
|
list.events(ctx, {
|
|
114
124
|
pluginInstalled: p => {
|
|
115
|
-
if (repos.includes(p.repo))
|
|
125
|
+
if (skipInitial || repos.includes(p.repo))
|
|
116
126
|
list.update({ id: p.repo }, { installed: true });
|
|
117
127
|
},
|
|
118
128
|
pluginUninstalled: (_folder, repo) => {
|
|
119
129
|
if (typeof repo !== 'string')
|
|
120
130
|
return; // custom repo
|
|
121
|
-
if (repos.includes(repo))
|
|
131
|
+
if (skipInitial || repos.includes(repo))
|
|
122
132
|
list.update({ id: repo }, { installed: false });
|
|
123
133
|
},
|
|
124
134
|
pluginDownload({ repo, status }) {
|
|
125
|
-
if (repos.includes(repo))
|
|
135
|
+
if (skipInitial || repos.includes(repo))
|
|
126
136
|
list.update({ id: repo }, { downloading: status ?? null });
|
|
127
137
|
}
|
|
128
138
|
});
|
|
139
|
+
if (skipInitial)
|
|
140
|
+
return list.ready();
|
|
141
|
+
let updatedAt;
|
|
142
|
+
let previous = new Map();
|
|
129
143
|
try {
|
|
130
|
-
const
|
|
131
|
-
|
|
144
|
+
for await (const snapshot of (0, pluginCatalog_1.getPluginCatalog)()) {
|
|
145
|
+
const already = Object.values((0, github_1.getFolder2repo)()).filter(Boolean).map(String);
|
|
146
|
+
const plugins = await (0, pluginCatalog_1.searchPluginCatalog)(snapshot, text, already);
|
|
132
147
|
if (ctx.isAborted())
|
|
133
148
|
return;
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
149
|
+
const rows = plugins.map(pl => {
|
|
150
|
+
const missing = (0, plugins_1.getMissingDependencies)(pl);
|
|
151
|
+
return { ...pl, missing: missing.length ? missing : null, downloading: github_1.downloading[pl.repo] ?? null };
|
|
152
|
+
});
|
|
153
|
+
// reconcile snapshots without dropping selection or retaining obsolete optional metadata
|
|
154
|
+
for (const id of previous.keys())
|
|
155
|
+
if (!rows.some(row => row.id === id))
|
|
156
|
+
list.remove({ id });
|
|
157
|
+
for (const row of rows) {
|
|
158
|
+
const old = previous.get(row.id);
|
|
159
|
+
if (old)
|
|
160
|
+
list.update({ id: row.id }, { ...lodash_1.default.mapValues(old, () => null), ...lodash_1.default.mapValues(row, value => value ?? null) });
|
|
161
|
+
else
|
|
162
|
+
list.add(row);
|
|
163
|
+
}
|
|
164
|
+
previous = new Map(rows.map(row => [row.id, row]));
|
|
165
|
+
repos.splice(0, repos.length, ...rows.map(row => row.repo));
|
|
166
|
+
updatedAt = snapshot.updatedAt;
|
|
167
|
+
list.props({ updatedAt, refreshing: snapshot.refreshing });
|
|
168
|
+
list.ready();
|
|
140
169
|
}
|
|
141
170
|
}
|
|
142
171
|
catch (err) {
|
|
172
|
+
list.props({ updatedAt, refreshing: false });
|
|
143
173
|
list.error(err.code || err.message);
|
|
144
174
|
}
|
|
145
175
|
list.ready();
|
|
@@ -177,12 +207,12 @@ const apis = {
|
|
|
177
207
|
(0, plugins_1.setPluginConfig)(id, null);
|
|
178
208
|
return {};
|
|
179
209
|
},
|
|
180
|
-
get_plugin_log({ id }, ctx) {
|
|
210
|
+
get_plugin_log({ id, skipInitial }, ctx) {
|
|
181
211
|
assertPluginId(id);
|
|
182
212
|
const p = (0, plugins_1.getPluginInfo)(id);
|
|
183
213
|
if (!p)
|
|
184
214
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
185
|
-
const list = new SendList_1.SendListReadable({ addAtStart: p.log });
|
|
215
|
+
const list = new SendList_1.SendListReadable({ addAtStart: skipInitial ? [] : p.log });
|
|
186
216
|
return list.events(ctx, {
|
|
187
217
|
['pluginLog:' + id]: x => list.add(x)
|
|
188
218
|
});
|
|
@@ -202,6 +232,10 @@ function serialize(p) {
|
|
|
202
232
|
: JSON.stringify(o.config, (_k, v) => lodash_1.default.isFunction(v) ? String(v) : v); // allow simple functions
|
|
203
233
|
return lodash_1.default.defaults(o, { started: null, badApi: null }); // nulls should be used to be sure to overwrite previous values,
|
|
204
234
|
}
|
|
235
|
+
function serializeRunningPlugin(id) {
|
|
236
|
+
const plugin = (0, plugins_1.firstPlugin)((plugin, pluginId) => pluginId === id ? plugin : undefined);
|
|
237
|
+
return plugin ? serialize(plugin) : new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
|
238
|
+
}
|
|
205
239
|
async function checkDependencies(plugin) {
|
|
206
240
|
const miss = await (0, plugins_1.getMissingDependencies)(plugin);
|
|
207
241
|
if (miss.length)
|
package/src/argv.js
CHANGED
|
@@ -6,3 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.argv = void 0;
|
|
7
7
|
const minimist_1 = __importDefault(require("minimist"));
|
|
8
8
|
exports.argv = (0, minimist_1.default)(process.argv.slice(2));
|
|
9
|
+
// Node's internal flag is read-only when enabled via its CLI, but remains configurable
|
|
10
|
+
Object.defineProperty(process, 'traceProcessWarnings', {
|
|
11
|
+
value: exports.argv['trace-warnings'] !== false, writable: true, configurable: true,
|
|
12
|
+
});
|
package/src/auth.js
CHANGED
|
@@ -115,8 +115,6 @@ async function setLoggedIn(ctx, username, via) {
|
|
|
115
115
|
ctx.throw(cross_const_1.HTTP_UNAUTHORIZED, error);
|
|
116
116
|
}
|
|
117
117
|
const normalized = (0, perm_1.normalizeUsername)(username);
|
|
118
|
-
if (s.username !== normalized)
|
|
119
|
-
delete s.allowNet; // discard restrictions cached for another identity before replacing the session account
|
|
120
118
|
s.username = normalized;
|
|
121
119
|
s.ts = Date.now();
|
|
122
120
|
s.ip = ctx.ip;
|
package/src/basicWeb.js
CHANGED
|
@@ -17,8 +17,12 @@ const autoBasic = (0, config_1.defineConfig)(misc_1.CFG.auto_basic, true, v => l
|
|
|
17
17
|
function basicWeb(ctx, node) {
|
|
18
18
|
const { get } = ctx.query;
|
|
19
19
|
if (get === 'login') {
|
|
20
|
-
if ((0, auth_1.getCurrentUsername)(ctx))
|
|
21
|
-
ctx.
|
|
20
|
+
if ((0, auth_1.getCurrentUsername)(ctx)) {
|
|
21
|
+
const referer = ctx.get('referer');
|
|
22
|
+
const url = referer && (0, misc_1.try_)(() => new URL(referer, ctx.URL));
|
|
23
|
+
// keep the origin in Location because a same-origin path starting with // would redirect externally
|
|
24
|
+
ctx.redirect(url && url.origin === ctx.URL.origin ? url.origin + url.pathname + url.search + url.hash : '/');
|
|
25
|
+
}
|
|
22
26
|
else {
|
|
23
27
|
ctx.set('WWW-Authenticate', cross_const_1.BASIC_AUTHENTICATE_HEADER);
|
|
24
28
|
ctx.status = cross_const_1.HTTP_UNAUTHORIZED;
|
package/src/block.js
CHANGED
|
@@ -26,7 +26,7 @@ exports.block = (0, config_1.defineConfig)(misc_1.CFG.block, [], rules => {
|
|
|
26
26
|
});
|
|
27
27
|
return ret;
|
|
28
28
|
});
|
|
29
|
-
function applyBlock(socket, ip = (0,
|
|
29
|
+
function applyBlock(socket, ip = (0, misc_1.normalizeIp)(socket.remoteAddress || '')) {
|
|
30
30
|
if (ip && isBlocked(ip))
|
|
31
31
|
return (0, connections_1.disconnect)(socket, 'block-ip');
|
|
32
32
|
}
|
package/src/config.js
CHANGED
|
@@ -244,6 +244,7 @@ Most common configurations:
|
|
|
244
244
|
For a description of each configuration, please refer to https://rejetto.com/hfs-config
|
|
245
245
|
Other options:
|
|
246
246
|
--debug will print extra information
|
|
247
|
+
--no-trace-warnings will disable stack traces for Node.js warnings (enabled by default)
|
|
247
248
|
`);
|
|
248
249
|
process.exit(0);
|
|
249
250
|
});
|
package/src/connections.js
CHANGED
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.disconnectionsLog = exports.Connection = void 0;
|
|
8
|
-
exports.normalizeIp = normalizeIp;
|
|
9
8
|
exports.newConnection = newConnection;
|
|
10
9
|
exports.getConnections = getConnections;
|
|
11
10
|
exports.socket2connection = socket2connection;
|
|
@@ -16,6 +15,7 @@ exports.disconnect = disconnect;
|
|
|
16
15
|
const events_1 = __importDefault(require("./events"));
|
|
17
16
|
const geo_1 = require("./geo");
|
|
18
17
|
const lodash_1 = __importDefault(require("lodash"));
|
|
18
|
+
const cross_1 = require("./cross");
|
|
19
19
|
class Connection {
|
|
20
20
|
socket;
|
|
21
21
|
started = new Date();
|
|
@@ -41,19 +41,16 @@ class Connection {
|
|
|
41
41
|
events_1.default.emit('connectionNewIp', this, this._cachedIp, this.ctx.ip);
|
|
42
42
|
this._cachedIp = undefined;
|
|
43
43
|
}
|
|
44
|
-
return this.ctx?.ip || (this._cachedIp ??= normalizeIp(this.socket.remoteAddress || ''));
|
|
44
|
+
return this.ctx?.ip || (this._cachedIp ??= (0, cross_1.normalizeIp)(this.socket.remoteAddress || ''));
|
|
45
45
|
}
|
|
46
46
|
get secure() {
|
|
47
47
|
return this.socket.server.cert > '';
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
exports.Connection = Connection;
|
|
51
|
-
function normalizeIp(ip) {
|
|
52
|
-
return ip.replace(/^::ffff:/, ''); // simplify ipv6-mapped addresses
|
|
53
|
-
}
|
|
54
51
|
const all = [];
|
|
55
52
|
function newConnection(socket) {
|
|
56
|
-
const ip = normalizeIp(socket.remoteAddress || '');
|
|
53
|
+
const ip = (0, cross_1.normalizeIp)(socket.remoteAddress || '');
|
|
57
54
|
const res = events_1.default.emit('newSocket', { socket, ip });
|
|
58
55
|
const msg = res?.isDefaultPrevented() ? 'plugin (newSocket)' : res?.find(lodash_1.default.isString);
|
|
59
56
|
if (!msg)
|
|
@@ -90,7 +87,7 @@ exports.disconnectionsLog = [];
|
|
|
90
87
|
function disconnect(what, logMessage = '') {
|
|
91
88
|
if ('socket' in what)
|
|
92
89
|
what = what.socket;
|
|
93
|
-
const ip = normalizeIp(what.remoteAddress || '');
|
|
90
|
+
const ip = (0, cross_1.normalizeIp)(what.remoteAddress || '');
|
|
94
91
|
if (logMessage)
|
|
95
92
|
console.debug("Disconnection:", logMessage, ip);
|
|
96
93
|
(0, geo_1.ip2country)(ip).then(res => {
|
package/src/const.js
CHANGED
|
@@ -49,7 +49,7 @@ const path_1 = require("path");
|
|
|
49
49
|
const cross_1 = require("./cross");
|
|
50
50
|
const argv_1 = require("./argv");
|
|
51
51
|
__exportStar(require("./cross-const"), exports);
|
|
52
|
-
exports.API_VERSION = 13.
|
|
52
|
+
exports.API_VERSION = 13.6;
|
|
53
53
|
exports.COMPATIBLE_API_VERSION = 1; // the day we break with the past, we'll update this
|
|
54
54
|
// you can add arguments with this file, currently used for the update process on mac/linux.
|
|
55
55
|
// we are using homedir because it's the only stable path both the old and new process can agree on (open() doesn't preserve cwd)
|
package/src/cross-const.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HTTP_MESSAGES = exports.HTTP_INSUFFICIENT_STORAGE = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_LOCKED = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_LENGTH_REQUIRED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_CREATED = exports.HTTP_OK = exports.CFG = exports.MB = exports.KB = exports.BASIC_AUTHENTICATE_HEADER = exports.EMBEDDED_LANGUAGE = exports.MASK_IN_TESTS = exports.HIDE_IN_TESTS = exports.ALLOW_SESSION_IP_CHANGE = exports.PREVIOUS_TAG = exports.MTIME_CHECK = exports.UPLOAD_TEMP_HASH = exports.UPLOAD_TEMP_PREFIX = exports.HFS_REPO = exports.PLUGIN_CUSTOM_REST_PREFIX = exports.NBSP = exports.PORT_DISABLED = exports.ICONS_URI = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = void 0;
|
|
3
|
+
exports.HTTP_MESSAGES = exports.HTTP_INSUFFICIENT_STORAGE = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_LOCKED = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_LENGTH_REQUIRED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_TEMPORARY_REDIRECT_KEEP_METHOD = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_CREATED = exports.HTTP_OK = exports.CFG = exports.MB = exports.KB = exports.BASIC_AUTHENTICATE_HEADER = exports.EMBEDDED_LANGUAGE = exports.MASK_IN_TESTS = exports.HIDE_IN_TESTS = exports.ALLOW_SESSION_IP_CHANGE = exports.PREVIOUS_TAG = exports.MTIME_CHECK = exports.UPLOAD_TEMP_HASH = exports.UPLOAD_TEMP_PREFIX = exports.PLUGIN_USAGE_URL = exports.HFS_REPO = exports.PLUGIN_CUSTOM_REST_PREFIX = exports.NBSP = exports.PORT_DISABLED = exports.ICONS_URI = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = void 0;
|
|
4
4
|
exports.SPECIAL_URI = '/~/';
|
|
5
5
|
exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
|
6
6
|
exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
|
|
@@ -11,6 +11,7 @@ exports.PORT_DISABLED = -1;
|
|
|
11
11
|
exports.NBSP = '\xA0';
|
|
12
12
|
exports.PLUGIN_CUSTOM_REST_PREFIX = '_';
|
|
13
13
|
exports.HFS_REPO = 'rejetto/hfs';
|
|
14
|
+
exports.PLUGIN_USAGE_URL = 'https://hfs-api.rejetto.com/plugin-usage';
|
|
14
15
|
exports.UPLOAD_TEMP_PREFIX = 'hfs$upload-';
|
|
15
16
|
exports.UPLOAD_TEMP_HASH = 'upload-temp-hash';
|
|
16
17
|
exports.MTIME_CHECK = 'x-mtime-check';
|
|
@@ -18,12 +19,12 @@ exports.PREVIOUS_TAG = 'previous';
|
|
|
18
19
|
exports.ALLOW_SESSION_IP_CHANGE = 'allow_session_ip_change';
|
|
19
20
|
exports.HIDE_IN_TESTS = 'hideInTests'; // elements that have variable size, where masking would produce changes, must be hidden
|
|
20
21
|
exports.MASK_IN_TESTS = 'maskInTests';
|
|
21
|
-
exports.EMBEDDED_LANGUAGE = 'en';
|
|
22
|
+
exports.EMBEDDED_LANGUAGE = 'en';
|
|
22
23
|
exports.BASIC_AUTHENTICATE_HEADER = 'Basic realm="HFS"';
|
|
23
24
|
exports.KB = 1024;
|
|
24
25
|
exports.MB = exports.KB * exports.KB;
|
|
25
26
|
exports.CFG = constMap([
|
|
26
|
-
'accounts', 'acme_domain', 'acme_renew', 'admin_net', 'allowed_referer',
|
|
27
|
+
'accounts', 'acme_domain', 'acme_renew', 'acme_challenge', 'acme_dns', 'admin_lang', 'admin_net', 'allowed_referer',
|
|
27
28
|
'authorization_header', 'auto_basic', 'auto_check_update', 'auto_play_seconds',
|
|
28
29
|
'base_url', 'block', 'cache_control_disk_files', 'cert', 'comments_storage',
|
|
29
30
|
'create-admin', 'debug', 'delete_unfinished_uploads_after',
|
|
@@ -33,12 +34,12 @@ exports.CFG = constMap([
|
|
|
33
34
|
'force_https', 'force_lang', 'force_webdav_login',
|
|
34
35
|
'geo_allow', 'geo_allow_unknown', 'geo_enable', 'geo_ignore_net', 'geo_list',
|
|
35
36
|
'ignore_proxies', 'invert_order', 'keep_session_alive', 'listen_interface',
|
|
36
|
-
'localhost_admin', 'log', 'log_api', 'log_gui', 'log_host', 'log_rotation', 'log_spam',
|
|
37
|
+
'localhost_admin', 'log', 'log_api', 'log_gui', 'log_host', 'log_rotation', 'log_spam', 'log_spam_regex',
|
|
37
38
|
'log_ua', 'mapped_port', 'max_downloads', 'max_downloads_per_account',
|
|
38
39
|
'max_downloads_per_ip', 'max_kbps', 'max_kbps_per_ip', 'menu_at_top', 'mime',
|
|
39
40
|
'min_available_mb', 'open_browser_at_start', 'outbound_interface', 'outbound_proxy', 'own_upload_delete_hours',
|
|
40
41
|
'page_size', 'plugins_config', 'port', 'private_key', 'proxies', 'roots', 'server_code',
|
|
41
|
-
'session_duration', 'show_hidden_files', 'show_uploader', 'size_1024',
|
|
42
|
+
'session_duration', 'share_usage_stats', 'show_hidden_files', 'show_uploader', 'size_1024',
|
|
42
43
|
'smart_unc_folder_detection', 'sort_by', 'sort_numerics', 'split_uploads',
|
|
43
44
|
'suspend_plugins', 'theme', 'tile_size', 'title', 'title_with_path', 'track_ips',
|
|
44
45
|
'update_to_beta', 'upnp_enabled', 'version', 'vfs', 'webdav_initial_auth',
|
|
@@ -54,6 +55,7 @@ exports.HTTP_PARTIAL_CONTENT = 206;
|
|
|
54
55
|
exports.HTTP_MOVED_PERMANENTLY = 301;
|
|
55
56
|
exports.HTTP_TEMPORARY_REDIRECT = 302;
|
|
56
57
|
exports.HTTP_NOT_MODIFIED = 304;
|
|
58
|
+
exports.HTTP_TEMPORARY_REDIRECT_KEEP_METHOD = 307;
|
|
57
59
|
exports.HTTP_BAD_REQUEST = 400;
|
|
58
60
|
exports.HTTP_UNAUTHORIZED = 401;
|
|
59
61
|
exports.HTTP_FORBIDDEN = 403;
|
package/src/cross.js
CHANGED
|
@@ -77,6 +77,7 @@ exports.xlate = xlate;
|
|
|
77
77
|
exports.normalizeHost = normalizeHost;
|
|
78
78
|
exports.isIpLocalHost = isIpLocalHost;
|
|
79
79
|
exports.isIpLan = isIpLan;
|
|
80
|
+
exports.normalizeIp = normalizeIp;
|
|
80
81
|
exports.ipForUrl = ipForUrl;
|
|
81
82
|
exports.escapeHTML = escapeHTML;
|
|
82
83
|
exports.promiseBestEffort = promiseBestEffort;
|
|
@@ -460,7 +461,20 @@ function isIpLocalHost(ip) {
|
|
|
460
461
|
return ip === '::1' || /^(?:::ffff:)?127\.0\.0\.1$/i.test(ip);
|
|
461
462
|
}
|
|
462
463
|
function isIpLan(ip) {
|
|
463
|
-
return /^(?:10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|
|
|
464
|
+
return /^(?:10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|169\.254\.|f[cd][\da-f]{2}:|fe[89ab][\da-f]:)/i.test(normalizeIp(ip));
|
|
465
|
+
}
|
|
466
|
+
function normalizeIp(ip) {
|
|
467
|
+
// proxy headers may contain invalid addresses; do not interpret URL paths or credentials as an IP
|
|
468
|
+
if (!/^[\da-f:.\[\]]+$/i.test(ip))
|
|
469
|
+
return ip;
|
|
470
|
+
// URL canonicalizes IPv6 and alternate IPv4 spellings before matching
|
|
471
|
+
const host = try_(() => new URL(`http://${ip.includes(':') && !ip.startsWith('[') ? `[${ip}]` : ip}`).hostname) || ip;
|
|
472
|
+
const bare = host.startsWith('[') ? host.slice(1, -1) : host;
|
|
473
|
+
if (bare.startsWith('::ffff:')) {
|
|
474
|
+
const words = bare.slice(7).split(':').map(x => parseInt(x, 16));
|
|
475
|
+
return words.flatMap(x => [x >> 8, x & 255]).join('.');
|
|
476
|
+
}
|
|
477
|
+
return bare;
|
|
464
478
|
}
|
|
465
479
|
function ipForUrl(ip) {
|
|
466
480
|
if (ip.startsWith('['))
|