hfs 3.1.5 → 3.2.0-beta2
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/af-DWEYq388.js +1 -0
- package/admin/assets/am-DgLbAgj6.js +1 -0
- package/admin/assets/ar-DgEWkO74.js +1 -0
- package/admin/assets/ar-dz-DHq--Sr8.js +1 -0
- package/admin/assets/ar-iq-D1r3nsb9.js +1 -0
- package/admin/assets/ar-kw-C85fGHwp.js +1 -0
- package/admin/assets/ar-ly-Bq6pjGjs.js +1 -0
- package/admin/assets/ar-ma-SGvmh6Mj.js +1 -0
- package/admin/assets/ar-sa-Bv8hiFi6.js +1 -0
- package/admin/assets/ar-tn-Bdvo77v3.js +1 -0
- package/admin/assets/az-7fPndoov.js +1 -0
- package/admin/assets/be-wjXeIeAK.js +1 -0
- package/admin/assets/bg-DZwBvjzH.js +1 -0
- package/admin/assets/bi-qCtxvMhO.js +1 -0
- package/admin/assets/bm-BVPWvreb.js +1 -0
- package/admin/assets/bn-CdWvye7d.js +1 -0
- package/admin/assets/bn-bd-B5-3blmz.js +1 -0
- package/admin/assets/bo-BEVcgyN2.js +1 -0
- package/admin/assets/br-DGQD6fFs.js +1 -0
- package/admin/assets/bs-So4MoRua.js +1 -0
- package/admin/assets/ca-Bk5ytG4g.js +1 -0
- package/admin/assets/cs-C4NU8eW-.js +1 -0
- package/admin/assets/cv-Ct-s-zrW.js +1 -0
- package/admin/assets/cy-ojtDPj8_.js +1 -0
- package/admin/assets/da-CYGin6vm.js +1 -0
- package/admin/assets/de-BEbJ01zH.js +1 -0
- package/admin/assets/de-at-C7UwE1rJ.js +1 -0
- package/admin/assets/de-ch-CsZ7YQc_.js +1 -0
- package/admin/assets/dv-DaVliwLd.js +1 -0
- package/admin/assets/el-DM_KqKEP.js +1 -0
- package/admin/assets/en-DedtOfaf.js +1 -0
- package/admin/assets/en-au-52Bzk5D9.js +1 -0
- package/admin/assets/en-ca-3pzEPK2N.js +1 -0
- package/admin/assets/en-gb-BrwDQS2G.js +1 -0
- package/admin/assets/en-ie-BUXSHrkL.js +1 -0
- package/admin/assets/en-il-a22drDCn.js +1 -0
- package/admin/assets/en-in-BUjecjkp.js +1 -0
- package/admin/assets/en-nz-Bbo7tnB_.js +1 -0
- package/admin/assets/en-sg-CZVDddmd.js +1 -0
- package/admin/assets/en-tt-DmSGwRia.js +1 -0
- package/admin/assets/eo-B71nkHZU.js +1 -0
- package/admin/assets/es-Dk6VCuuk.js +1 -0
- package/admin/assets/es-do-DMErY8ol.js +1 -0
- package/admin/assets/es-mx-BMRmqa3u.js +1 -0
- package/admin/assets/es-pr-CtBQz48p.js +1 -0
- package/admin/assets/es-us-CrDl5pnO.js +1 -0
- package/admin/assets/et-CO9OHqio.js +1 -0
- package/admin/assets/eu-Bip44atW.js +1 -0
- package/admin/assets/fa-CHbJ_dTM.js +1 -0
- package/admin/assets/fi-DKfoLmaQ.js +1 -0
- package/admin/assets/fo-DG1kOEfw.js +1 -0
- package/admin/assets/fr-DV73GZR4.js +1 -0
- package/admin/assets/fr-ca-BK-RoZiC.js +1 -0
- package/admin/assets/fr-ch-DjqEC5E_.js +1 -0
- package/admin/assets/fy-znrRQdeC.js +1 -0
- package/admin/assets/ga-BlZeKu0N.js +1 -0
- package/admin/assets/gd-BmrycMnC.js +1 -0
- package/admin/assets/gl-CuT8e5mi.js +1 -0
- package/admin/assets/gom-latn-BSWVd0A6.js +1 -0
- package/admin/assets/gu-BHK6LfvD.js +1 -0
- package/admin/assets/he-DPoTUevR.js +1 -0
- package/admin/assets/hi-BRuLafoW.js +1 -0
- package/admin/assets/hr-Bzge-10P.js +1 -0
- package/admin/assets/ht-Ck9BCna1.js +1 -0
- package/admin/assets/hu-CzqqbYmU.js +1 -0
- package/admin/assets/hy-am-C-eV4E8v.js +1 -0
- package/admin/assets/id-Dv8GZQvB.js +1 -0
- package/admin/assets/{index-DTxjaflW.js → index-BPIX0qPj.js} +1 -1
- package/admin/assets/index-CFWd-FDo.css +1 -0
- package/admin/assets/index-D3HviM6x.js +889 -0
- package/admin/assets/is-CK6VY3M_.js +1 -0
- package/admin/assets/it-1gtki4a5.js +1 -0
- package/admin/assets/it-ch-C0Mj3-pC.js +1 -0
- package/admin/assets/ja-Dl3AfnM1.js +1 -0
- package/admin/assets/jv-CznX-tGV.js +1 -0
- package/admin/assets/ka-BNjZxCug.js +1 -0
- package/admin/assets/kk-80J_xldf.js +1 -0
- package/admin/assets/km-CuWChDRB.js +1 -0
- package/admin/assets/kn-BZp_PBdl.js +1 -0
- package/admin/assets/ko-RirpyUl_.js +1 -0
- package/admin/assets/ku-Dz8ACD5w.js +1 -0
- package/admin/assets/ky-BN3ylOhj.js +1 -0
- package/admin/assets/lb-D7h_YoEn.js +1 -0
- package/admin/assets/lo-BrlbTUPD.js +1 -0
- package/admin/assets/lt-cXuHFdTa.js +1 -0
- package/admin/assets/lv-CjIpv13Q.js +1 -0
- package/admin/assets/me-B-jTh39Z.js +1 -0
- package/admin/assets/mi-D06xdVmt.js +1 -0
- package/admin/assets/mk-iLbuxyOf.js +1 -0
- package/admin/assets/ml-2W0y6Zb2.js +1 -0
- package/admin/assets/mn-6zbvKjeb.js +1 -0
- package/admin/assets/mr-7-jrgLyw.js +1 -0
- package/admin/assets/ms-CRH6rEXt.js +1 -0
- package/admin/assets/ms-my-BIJmu2S-.js +1 -0
- package/admin/assets/mt-CbXmxK-D.js +1 -0
- package/admin/assets/my-BvMUsxU8.js +1 -0
- package/admin/assets/nb-DvKHgF7L.js +1 -0
- package/admin/assets/ne-DiZZ3Lm6.js +1 -0
- package/admin/assets/nl-be-Dy7PYRbC.js +1 -0
- package/admin/assets/nl-t_2A_VAT.js +1 -0
- package/admin/assets/nn-Cw7EwosO.js +1 -0
- package/admin/assets/oc-lnc-CuFfB75K.js +1 -0
- package/admin/assets/pa-in-DOFyZ-Ft.js +1 -0
- package/admin/assets/pl-BD7FyCJj.js +1 -0
- package/admin/assets/pt-DSKLLE_u.js +1 -0
- package/admin/assets/pt-br-BhL4gb5Z.js +1 -0
- package/admin/assets/rn-DoHoZZPd.js +1 -0
- package/admin/assets/ro-B0v-_lH0.js +1 -0
- package/admin/assets/ru-BMVOk5eA.js +1 -0
- package/admin/assets/rw-CWF0w6eL.js +1 -0
- package/admin/assets/sd-DO2rrjch.js +1 -0
- package/admin/assets/se-CAolO9WQ.js +1 -0
- package/admin/assets/{sha512-D936QW8l.js → sha512-ZlUYj4Hr.js} +1 -1
- package/admin/assets/si-D03dHfb5.js +1 -0
- package/admin/assets/sk-_GcZGaN3.js +1 -0
- package/admin/assets/sl-Cb1lUGab.js +1 -0
- package/admin/assets/sq-Czzt23Tr.js +1 -0
- package/admin/assets/sr-D76dVqKJ.js +1 -0
- package/admin/assets/sr-cyrl-CuoFbJjW.js +1 -0
- package/admin/assets/ss-g-fGaM29.js +1 -0
- package/admin/assets/sv-CZxc8I45.js +1 -0
- package/admin/assets/sv-fi-D8REJeLz.js +1 -0
- package/admin/assets/sw-B1n3PjWG.js +1 -0
- package/admin/assets/ta-K5mexJNT.js +1 -0
- package/admin/assets/te-DT6dj5B6.js +1 -0
- package/admin/assets/tet-DXwYNm_H.js +1 -0
- package/admin/assets/tg-BCcZKcE2.js +1 -0
- package/admin/assets/th-CeeeseFX.js +1 -0
- package/admin/assets/tk-CJ6KW44d.js +1 -0
- package/admin/assets/tl-ph-DzE8lDmm.js +1 -0
- package/admin/assets/tlh-ER6KiMxG.js +1 -0
- package/admin/assets/tr-D48NGNpr.js +1 -0
- package/admin/assets/tzl-5AsmDTYM.js +1 -0
- package/admin/assets/tzm-2AzZ1YPf.js +1 -0
- package/admin/assets/tzm-latn-Cd5hPQuT.js +1 -0
- package/admin/assets/ug-cn-DU-MZ9Vx.js +1 -0
- package/admin/assets/uk-hn6vcOkn.js +1 -0
- package/admin/assets/ur-jOObtqB6.js +1 -0
- package/admin/assets/uz-BlftYfHF.js +1 -0
- package/admin/assets/uz-latn-BpuI0ccM.js +1 -0
- package/admin/assets/vi-DVo3LusT.js +1 -0
- package/admin/assets/x-pseudo-BuVzNhqi.js +1 -0
- package/admin/assets/yo-BvWGwb4m.js +1 -0
- package/admin/assets/zh-D9-tfba1.js +1 -0
- package/admin/assets/zh-cn-CFL5sbIW.js +1 -0
- package/admin/assets/zh-hk-DRP8u65r.js +1 -0
- package/admin/assets/zh-tw-CtVs1ihI.js +1 -0
- package/admin/index.html +2 -2
- package/frontend/assets/index-legacy-D3BTBYs5.js +9 -0
- package/frontend/assets/{index-legacy-vmpqwZZf.js → index-legacy-DcrWtKxQ.js} +1 -1
- package/frontend/assets/{sha512-legacy-wI89-UHR.js → sha512-legacy-DJvEwScE.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +144 -71
- package/package.json +9 -9
- package/plugins/antibrute/plugin.js +150 -19
- package/plugins/list-uploader/public/main.js +1 -1
- package/src/acme.js +11 -7
- package/src/api.accounts.js +3 -3
- package/src/api.auth.js +3 -1
- package/src/api.get_file_list.js +17 -13
- package/src/api.monitor.js +47 -43
- package/src/api.net.js +4 -3
- package/src/api.vfs.js +1 -1
- package/src/basicWeb.js +1 -1
- package/src/commands.js +54 -1
- package/src/comments.js +7 -4
- package/src/config.js +9 -5
- package/src/consoleLog.js +39 -1
- package/src/const.js +4 -0
- package/src/cross.js +33 -6
- package/src/errorPages.js +20 -10
- package/src/events.js +3 -3
- package/src/expiringCache.js +8 -7
- package/src/fileAttr.js +73 -15
- package/src/frontEndApis.js +20 -15
- package/src/index.js +2 -1
- package/src/langs/hfs-lang-ar.json +2 -1
- package/src/langs/hfs-lang-bg.json +2 -1
- package/src/langs/hfs-lang-de.json +2 -1
- package/src/langs/hfs-lang-el.json +2 -1
- package/src/langs/hfs-lang-es.json +2 -1
- package/src/langs/hfs-lang-fi.json +2 -1
- package/src/langs/hfs-lang-fr.json +2 -1
- package/src/langs/hfs-lang-hu.json +2 -1
- package/src/langs/hfs-lang-it.json +2 -1
- package/src/langs/hfs-lang-ja.json +2 -1
- package/src/langs/hfs-lang-ko.json +2 -1
- package/src/langs/hfs-lang-lt.json +2 -1
- package/src/langs/hfs-lang-ms.json +2 -0
- package/src/langs/hfs-lang-nl.json +2 -1
- package/src/langs/hfs-lang-pt-br.json +2 -1
- package/src/langs/hfs-lang-ro.json +2 -1
- package/src/langs/hfs-lang-ru.json +2 -1
- package/src/langs/hfs-lang-sr-latn.json +2 -1
- package/src/langs/hfs-lang-sr.json +2 -1
- package/src/langs/hfs-lang-th.json +2 -1
- package/src/langs/hfs-lang-tr.json +2 -1
- package/src/langs/hfs-lang-uk.json +2 -1
- package/src/langs/hfs-lang-vi.json +2 -1
- package/src/langs/hfs-lang-zh-tw.json +2 -1
- package/src/langs/hfs-lang-zh.json +2 -1
- package/src/listen.js +2 -1
- package/src/log.js +27 -2
- package/src/middlewares.js +8 -2
- package/src/misc.js +14 -0
- package/src/nat.js +61 -21
- package/src/outboundProxy.js +1 -1
- package/src/perm.js +5 -1
- package/src/plugins.js +34 -5
- package/src/roots.js +1 -1
- package/src/selfCheck.js +2 -1
- package/src/serveGuiAndSharedFiles.js +18 -7
- package/src/serveGuiFiles.js +2 -1
- package/src/update.js +10 -18
- package/src/urlList.js +32 -0
- package/src/util-files.js +24 -9
- package/src/util-http.js +4 -0
- package/src/vfs.js +21 -9
- package/src/walkDir.js +7 -1
- package/src/webdav.js +4 -1
- package/src/zip.js +3 -2
- package/admin/assets/index-B66w-a0v.css +0 -1
- package/admin/assets/index-Df6vYR7s.js +0 -822
- package/frontend/assets/index-legacy-emBsvICj.js +0 -9
package/src/acme.js
CHANGED
|
@@ -32,17 +32,21 @@ const acmeMiddleware = (ctx, next) => {
|
|
|
32
32
|
return next();
|
|
33
33
|
};
|
|
34
34
|
exports.acmeMiddleware = acmeMiddleware;
|
|
35
|
-
const TEMP_MAP =
|
|
35
|
+
const TEMP_MAP = (0, nat_1.upnpMappingParam)(80, 80, 'hfs temporary', 5000); // from my tests (zyxel VMG8825), lower values won't make a working mapping
|
|
36
|
+
// remove temporary port mapping, if any is left from previous execution
|
|
36
37
|
(0, misc_1.repeat)(misc_1.MINUTE, async (stop) => {
|
|
37
|
-
await nat_1.
|
|
38
|
-
|
|
38
|
+
if (!await nat_1.upnpEnabled.getWhenReady())
|
|
39
|
+
return stop();
|
|
40
|
+
const client = (0, nat_1.getUpnpClient)();
|
|
41
|
+
await client.getGateway(); // without this, the next call will break upnp support
|
|
42
|
+
const res = await client.getMappings();
|
|
39
43
|
const leftover = res.find(x => x.description === TEMP_MAP.description); // in case the process is interrupted
|
|
40
44
|
if (!leftover)
|
|
41
45
|
return void stop(); // we are good
|
|
42
46
|
if (acmeOngoing)
|
|
43
47
|
return; // it doesn't count, as we are in the middle of something. Retry later
|
|
44
48
|
stop();
|
|
45
|
-
return
|
|
49
|
+
return client.removeMapping(TEMP_MAP);
|
|
46
50
|
});
|
|
47
51
|
async function generateSSLCert(domain, email, altNames) {
|
|
48
52
|
// will answer the challenge through our koa app (if on port 80) or must we spawn a dedicated server?
|
|
@@ -63,7 +67,7 @@ async function generateSSLCert(domain, email, altNames) {
|
|
|
63
67
|
let check = await (0, selfCheck_1.selfCheck)(checkUrl); // some check services may not consider the domain, but we already verified that
|
|
64
68
|
if (check?.success === false && nat.upnp && !nat.mapped80) {
|
|
65
69
|
console.debug("Setting temporary port forward");
|
|
66
|
-
tempMap = await (0, misc_1.haveTimeout)(10_000, nat_1.
|
|
70
|
+
tempMap = await (0, misc_1.haveTimeout)(10_000, (0, nat_1.getUpnpClient)().createMapping(TEMP_MAP)).catch(() => { });
|
|
67
71
|
check = await (0, selfCheck_1.selfCheck)(checkUrl); // repeat test
|
|
68
72
|
}
|
|
69
73
|
//if (!check) throw new ApiError(HTTP_FAILED_DEPENDENCY, "couldn't test port 80")
|
|
@@ -88,9 +92,9 @@ async function generateSSLCert(domain, email, altNames) {
|
|
|
88
92
|
return { key, cert };
|
|
89
93
|
}
|
|
90
94
|
finally {
|
|
91
|
-
if (tempMap) {
|
|
95
|
+
if (tempMap && nat_1.upnpEnabled.get()) {
|
|
92
96
|
console.debug("Removing temporary port forward");
|
|
93
|
-
nat_1.
|
|
97
|
+
(0, nat_1.getUpnpClient)().removeMapping(TEMP_MAP).catch(() => { }); // clean after ourselves
|
|
94
98
|
}
|
|
95
99
|
acmeOngoing = false;
|
|
96
100
|
if (tempSrv)
|
package/src/api.accounts.js
CHANGED
|
@@ -16,9 +16,9 @@ function prepareAccount(ac) {
|
|
|
16
16
|
...lodash_1.default.omit(ac, ['password', 'hashed_password', 'srp']),
|
|
17
17
|
username: ac.username, // omit won't copy it because it's a hidden prop
|
|
18
18
|
hasPassword: (0, perm_1.accountHasPassword)(ac),
|
|
19
|
-
isGroup: !
|
|
19
|
+
isGroup: !(0, perm_1.accountHasLoginMethod)(ac),
|
|
20
20
|
adminActualAccess: (0, perm_1.accountCanLoginAdmin)(ac),
|
|
21
|
-
canLogin: (0, perm_1.
|
|
21
|
+
canLogin: (0, perm_1.accountHasLoginMethod)(ac) ? (0, perm_1.accountCanLogin)(ac) : undefined,
|
|
22
22
|
canChangePassword: (0, perm_1.accountCanChangePassword)(ac),
|
|
23
23
|
invalidated: auth_1.invalidateSessionBefore.get(ac.username),
|
|
24
24
|
directMembers: Object.values(perm_1.accounts.get()).filter(a => a.belongs?.includes(ac.username)).map(x => x.username),
|
|
@@ -33,7 +33,7 @@ function prepareAccount(ac) {
|
|
|
33
33
|
})
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
const ALLOWED_KEYS = ['admin', 'allow_net', 'belongs', 'days_to_live', 'disable_password_change',
|
|
36
|
+
const ALLOWED_KEYS = ['admin', 'allow_net', 'auto_login_net', 'belongs', 'days_to_live', 'disable_password_change',
|
|
37
37
|
'disabled', 'expire', 'ignore_limits', 'notes', 'password', 'redirect', 'require_password_change', 'username'];
|
|
38
38
|
exports.default = {
|
|
39
39
|
get_usernames() {
|
package/src/api.auth.js
CHANGED
|
@@ -18,10 +18,12 @@ const ongoingLogins = {}; // store data that doesn't fit session object
|
|
|
18
18
|
const keepSessionAlive = (0, config_1.defineConfig)('keep_session_alive', true);
|
|
19
19
|
const refresh_session = async ({}, ctx) => {
|
|
20
20
|
const username = (0, auth_1.getCurrentUsername)(ctx);
|
|
21
|
+
const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx) || undefined;
|
|
21
22
|
return !ctx.session ? new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR) : {
|
|
22
23
|
username,
|
|
23
24
|
expandedUsername: Array.from((0, perm_1.expandUsername)(username)),
|
|
24
|
-
|
|
25
|
+
isAdmin,
|
|
26
|
+
adminUrl: isAdmin && ctx.state.revProxyPath + const_1.ADMIN_URI,
|
|
25
27
|
canChangePassword: (0, perm_1.accountCanChangePassword)(ctx.state.account),
|
|
26
28
|
requireChangePassword: ctx.state.account?.require_password_change,
|
|
27
29
|
exp: username && keepSessionAlive.get() ? new Date(Date.now() + middlewares_1.sessionDuration.compiled()) : undefined,
|
package/src/api.get_file_list.js
CHANGED
|
@@ -35,7 +35,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
35
35
|
if (!node)
|
|
36
36
|
return fail(const_1.HTTP_NOT_FOUND);
|
|
37
37
|
admin &&= (0, adminApis_1.ctxAdminAccess)(ctx); // validate 'admin' flag
|
|
38
|
-
if (await (0, vfs_1.
|
|
38
|
+
if (await (0, vfs_1.getDefaultFile)(node, ctx) || !(0, vfs_1.nodeIsFolder)(node)) // for files without permission, the frontend is sent, and the location is the file itself
|
|
39
39
|
// so, we first check if you have a permission problem, to tell frontend to show login, otherwise we fall back to method_not_allowed, as it's proper for files.
|
|
40
40
|
return fail(!admin && (0, vfs_1.statusCodeForMissingPerm)(node, 'can_read', ctx) ? undefined : const_1.HTTP_METHOD_NOT_ALLOWED);
|
|
41
41
|
if (!admin && (0, vfs_1.statusCodeForMissingPerm)(node, 'can_list', ctx))
|
|
@@ -44,7 +44,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
44
44
|
limit = Number(limit);
|
|
45
45
|
const { filterName, filterComment, fileMask, depth } = paramsToFilter(rest);
|
|
46
46
|
const walker = (0, vfs_1.walkNode)(node, { ctx: admin ? undefined : ctx, onlyFolders, onlyFiles, depth });
|
|
47
|
-
const onDirEntryHandlers = (0, plugins_1.mapPlugins)(plug => plug.onDirEntry);
|
|
47
|
+
const onDirEntryHandlers = (0, plugins_1.mapPlugins)((plug, id) => plug.onDirEntry && { id, cb: plug.onDirEntry });
|
|
48
48
|
const can_upload = admin || (0, vfs_1.hasPermission)(node, 'can_upload', ctx);
|
|
49
49
|
const can_delete = admin || (0, vfs_1.hasPermission)(node, 'can_delete', ctx);
|
|
50
50
|
const fakeChild = await (0, vfs_1.applyParentToChild)({ source: 'dummy-file', original: undefined }, node); // used to check permission; simple but can produce false results; 'original' to simulate a non-vfs node
|
|
@@ -74,7 +74,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
74
74
|
async function* produceEntries() {
|
|
75
75
|
for await (const sub of walker) {
|
|
76
76
|
let name = (0, vfs_1.getNodeName)(sub);
|
|
77
|
-
name = (0, path_1.basename)(name) || name; // on
|
|
77
|
+
name = (0, path_1.basename)(name) || name; // on Windows, basename('C:') === ''
|
|
78
78
|
if (filterName && !filterName(name) || fileMask && !(0, vfs_1.nodeIsFolder)(sub) && !fileMask(name)
|
|
79
79
|
|| filterComment && !filterComment(await (0, comments_1.getCommentFor)(sub.source) || ''))
|
|
80
80
|
continue;
|
|
@@ -83,15 +83,15 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
83
83
|
continue;
|
|
84
84
|
const cbParams = { entry, ctx, listUri: uri, node: sub };
|
|
85
85
|
try {
|
|
86
|
-
const res = await Promise.all(onDirEntryHandlers.map(cb => cb(cbParams)));
|
|
86
|
+
const res = await Promise.all(onDirEntryHandlers.map(({ id, cb }) => Promise.resolve().then(() => cb(cbParams)).catch(error => { throw { id, error }; })));
|
|
87
87
|
if (res.some(x => x === false))
|
|
88
88
|
continue;
|
|
89
|
-
if ((await events_1.default.emitAsync('dirEntry', cbParams))?.isDefaultPrevented())
|
|
90
|
-
continue;
|
|
91
89
|
}
|
|
92
90
|
catch (e) {
|
|
93
|
-
console.warn(
|
|
91
|
+
console.warn(`Plugin ${e?.id || '?'} is causing problems on onDirEntry:`, e?.error ?? e);
|
|
94
92
|
}
|
|
93
|
+
if ((await events_1.default.emitAsync('dirEntry', cbParams))?.isDefaultPrevented())
|
|
94
|
+
continue;
|
|
95
95
|
if (offset) {
|
|
96
96
|
--offset;
|
|
97
97
|
continue;
|
|
@@ -111,10 +111,14 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
111
111
|
const name = (0, vfs_1.getNodeName)(node);
|
|
112
112
|
const isFolder = (0, vfs_1.nodeIsFolder)(node);
|
|
113
113
|
try {
|
|
114
|
-
const st =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
const [web, comment, st] = await Promise.all([
|
|
115
|
+
(0, vfs_1.getDefaultFile)(node, ctx).then(x => x ? true : undefined),
|
|
116
|
+
node.comment ?? (0, comments_1.getCommentFor)(source),
|
|
117
|
+
(0, vfs_1.nodeStats)(node).catch(e => {
|
|
118
|
+
if (!isFolder || !node.children?.length) // folders with virtual children, keep them
|
|
119
|
+
throw e;
|
|
120
|
+
})
|
|
121
|
+
]);
|
|
118
122
|
// permissions of entries are sent as a difference with permissions of parent
|
|
119
123
|
const pl = node.can_list === misc_1.WHO_NO_ONE ? 'l'
|
|
120
124
|
: !(0, vfs_1.hasPermission)(node, 'can_list', ctx) ? 'L'
|
|
@@ -136,9 +140,9 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
136
140
|
url,
|
|
137
141
|
target: node.target,
|
|
138
142
|
order: node.order,
|
|
139
|
-
comment
|
|
143
|
+
comment,
|
|
140
144
|
icon: getNodeIcon(node),
|
|
141
|
-
web
|
|
145
|
+
web,
|
|
142
146
|
};
|
|
143
147
|
}
|
|
144
148
|
catch {
|
package/src/api.monitor.js
CHANGED
|
@@ -4,6 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.serializeConnection = serializeConnection;
|
|
8
|
+
exports.inferOperation = inferOperation;
|
|
7
9
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
10
|
const connections_1 = require("./connections");
|
|
9
11
|
const misc_1 = require("./misc");
|
|
@@ -11,6 +13,7 @@ const throttler_1 = require("./throttler");
|
|
|
11
13
|
const auth_1 = require("./auth");
|
|
12
14
|
const SendList_1 = require("./SendList");
|
|
13
15
|
const persistence_1 = require("./persistence");
|
|
16
|
+
const cross_1 = require("./cross");
|
|
14
17
|
exports.default = {
|
|
15
18
|
async disconnect({ ip, port, allButLocalhost }) {
|
|
16
19
|
(0, misc_1.apiAssertTypes)({
|
|
@@ -28,25 +31,21 @@ exports.default = {
|
|
|
28
31
|
get_connections({}, ctx) {
|
|
29
32
|
const list = new SendList_1.SendListReadable({
|
|
30
33
|
diff: true,
|
|
31
|
-
addAtStart: (0, connections_1.getConnections)().map(
|
|
34
|
+
addAtStart: (0, connections_1.getConnections)().map(serializeConnection),
|
|
32
35
|
});
|
|
33
36
|
list.props({ you: ctx.ip });
|
|
34
37
|
return list.events(ctx, {
|
|
35
38
|
connection(conn) {
|
|
36
|
-
if (ignore(conn))
|
|
37
|
-
return;
|
|
38
39
|
list.add(serializeConnection(conn));
|
|
39
40
|
},
|
|
40
41
|
connectionClosed(conn) {
|
|
41
|
-
if (ignore(conn))
|
|
42
|
-
return;
|
|
43
42
|
list.remove(getConnAddress(conn));
|
|
44
43
|
},
|
|
45
44
|
connectionNewIp(conn, oldIp, newIp) {
|
|
46
45
|
list.update(getConnAddress(conn, oldIp), { ip: newIp });
|
|
47
46
|
},
|
|
48
47
|
connectionUpdated(conn, change) {
|
|
49
|
-
if (conn.socket.closed ||
|
|
48
|
+
if (conn.socket.closed || lodash_1.default.isEmpty(change))
|
|
50
49
|
return;
|
|
51
50
|
if (change.ctx) {
|
|
52
51
|
Object.assign(change, fromCtx(change.ctx));
|
|
@@ -55,48 +54,16 @@ exports.default = {
|
|
|
55
54
|
list.update(getConnAddress(conn), change);
|
|
56
55
|
},
|
|
57
56
|
});
|
|
58
|
-
function serializeConnection(conn) {
|
|
59
|
-
const { socket, started, secure } = conn;
|
|
60
|
-
return {
|
|
61
|
-
...getConnAddress(conn),
|
|
62
|
-
v: (socket.remoteFamily?.endsWith('6') ? 6 : 4),
|
|
63
|
-
got: socket.bytesRead,
|
|
64
|
-
sent: socket.bytesWritten,
|
|
65
|
-
country: conn.country,
|
|
66
|
-
started,
|
|
67
|
-
secure: (secure || undefined), // undefined will save some space once json-ed
|
|
68
|
-
...fromCtx(conn.ctx),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function fromCtx(ctx) {
|
|
72
|
-
if (!ctx)
|
|
73
|
-
return;
|
|
74
|
-
const s = ctx.state; // short alias
|
|
75
|
-
return {
|
|
76
|
-
user: (0, auth_1.getCurrentUsername)(ctx),
|
|
77
|
-
agent: (0, misc_1.shortenAgent)(ctx.get('user-agent')),
|
|
78
|
-
archive: s.archive,
|
|
79
|
-
...s.browsing ? { op: 'browsing', path: (0, misc_1.safeDecodeURIComponent)(s.browsing) }
|
|
80
|
-
: s.uploadPath ? { op: 'upload', path: (0, misc_1.safeDecodeURIComponent)(s.uploadPath) }
|
|
81
|
-
: {
|
|
82
|
-
op: !s.considerAsGui && (ctx.state.archive || ctx.state.vfsNode) ? 'download' : undefined,
|
|
83
|
-
path: (0, misc_1.safeDecodeURIComponent)(ctx.originalUrl),
|
|
84
|
-
},
|
|
85
|
-
opProgress: lodash_1.default.isNumber(s.opProgress) ? lodash_1.default.round(s.opProgress, 3) : undefined,
|
|
86
|
-
opTotal: s.opTotal,
|
|
87
|
-
opOffset: s.opOffset,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
57
|
},
|
|
91
58
|
async *get_connection_stats() {
|
|
92
59
|
while (1) {
|
|
93
|
-
const
|
|
60
|
+
const connections = (0, connections_1.getConnections)();
|
|
94
61
|
yield {
|
|
95
62
|
outSpeedKb: throttler_1.totalOutSpeedKb,
|
|
96
63
|
inSpeedKb: throttler_1.totalInSpeedKb,
|
|
97
64
|
sent_got: [throttler_1.totalSent.get(), throttler_1.totalGot.get(), totalGotSentResetTime.get()],
|
|
98
|
-
connections:
|
|
99
|
-
ips:
|
|
65
|
+
connections: connections.length,
|
|
66
|
+
ips: (0, cross_1.countUniqueBy)(connections, conn => conn.ip),
|
|
100
67
|
};
|
|
101
68
|
await (0, misc_1.wait)(1000);
|
|
102
69
|
}
|
|
@@ -108,8 +75,45 @@ exports.default = {
|
|
|
108
75
|
void persistence_1.storedMap.del(x);
|
|
109
76
|
},
|
|
110
77
|
};
|
|
111
|
-
function
|
|
112
|
-
|
|
78
|
+
function serializeConnection(conn) {
|
|
79
|
+
const { socket, started, secure } = conn;
|
|
80
|
+
return {
|
|
81
|
+
...getConnAddress(conn),
|
|
82
|
+
v: (socket.remoteFamily?.endsWith('6') ? 6 : 4),
|
|
83
|
+
// connection fields are request-scoped once transfer tracking starts; socket counters cover earlier snapshots
|
|
84
|
+
got: conn.got || socket.bytesRead,
|
|
85
|
+
sent: conn.sent || socket.bytesWritten,
|
|
86
|
+
outSpeedKb: conn.outSpeedKb,
|
|
87
|
+
inSpeedKb: conn.inSpeedKb,
|
|
88
|
+
country: conn.country,
|
|
89
|
+
started,
|
|
90
|
+
secure: (secure || undefined), // undefined will save some space once json-ed
|
|
91
|
+
...fromCtx(conn.ctx),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function fromCtx(ctx) {
|
|
95
|
+
if (!ctx)
|
|
96
|
+
return;
|
|
97
|
+
return {
|
|
98
|
+
user: (0, auth_1.getCurrentUsername)(ctx),
|
|
99
|
+
agent: (0, misc_1.shortenAgent)(ctx.get('user-agent')),
|
|
100
|
+
...inferOperation(ctx)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function inferOperation(ctx) {
|
|
104
|
+
const s = ctx.state; // short alias
|
|
105
|
+
return {
|
|
106
|
+
archive: s.archive,
|
|
107
|
+
...s.browsing ? { op: 'browsing', path: (0, misc_1.safeDecodeURIComponent)(s.browsing) }
|
|
108
|
+
: s.uploadPath ? { op: 'upload', path: (0, misc_1.safeDecodeURIComponent)(s.uploadPath) }
|
|
109
|
+
: {
|
|
110
|
+
op: !s.considerAsGui && (ctx.state.archive || ctx.state.vfsNode) ? 'download' : undefined,
|
|
111
|
+
path: (0, misc_1.safeDecodeURIComponent)(ctx.originalUrl),
|
|
112
|
+
},
|
|
113
|
+
opProgress: lodash_1.default.isNumber(s.opProgress) ? lodash_1.default.round(s.opProgress, 3) : undefined,
|
|
114
|
+
opTotal: s.opTotal,
|
|
115
|
+
opOffset: s.opOffset,
|
|
116
|
+
};
|
|
113
117
|
}
|
|
114
118
|
function getConnAddress(conn, overrideIp) {
|
|
115
119
|
return {
|
package/src/api.net.js
CHANGED
|
@@ -52,16 +52,17 @@ exports.default = {
|
|
|
52
52
|
return new apiMiddleware_1.ApiError(const_1.HTTP_FAILED_DEPENDENCY, "no internal port");
|
|
53
53
|
if (externalPort)
|
|
54
54
|
try {
|
|
55
|
-
await nat_1.
|
|
55
|
+
await (0, nat_1.getUpnpClient)().removeMapping({ public: { host: '', port: externalPort } });
|
|
56
56
|
}
|
|
57
57
|
catch (e) {
|
|
58
58
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, "removeMapping failed: " + String(e));
|
|
59
59
|
}
|
|
60
|
-
if (external)
|
|
61
|
-
await nat_1.
|
|
60
|
+
if (external)
|
|
61
|
+
await (0, nat_1.getUpnpClient)().createMapping((0, nat_1.upnpMappingParam)(internal || internalPort, external))
|
|
62
62
|
.catch(res => {
|
|
63
63
|
throw new apiMiddleware_1.ApiError(res.errorCode || const_1.HTTP_SERVER_ERROR, res.errorCode === 718 ? "Port not available" : res.errorDescription || res.message || "unknown error");
|
|
64
64
|
});
|
|
65
|
+
nat_1.mappedPort.set(external || 0); // remember only successful HFS mappings
|
|
65
66
|
return {};
|
|
66
67
|
},
|
|
67
68
|
async self_check({ url }) {
|
package/src/api.vfs.js
CHANGED
|
@@ -225,7 +225,7 @@ exports.default = {
|
|
|
225
225
|
for (const k of ['*', 'Directory']) {
|
|
226
226
|
await (0, util_os_1.reg)('add', WINDOWS_REG_KEY.replace('*', k), '/ve', '/f', '/d', 'Add to HFS (new)');
|
|
227
227
|
await (0, util_os_1.reg)('add', WINDOWS_REG_KEY.replace('*', k), '/v', 'icon', '/f', '/d', const_1.IS_BINARY ? process.execPath : const_1.APP_PATH + '\\hfs.ico');
|
|
228
|
-
await (0, util_os_1.reg)('add', WINDOWS_REG_KEY.replace('*', k) + '\\command', '/ve', '/f', '/d', `powershell -WindowStyle Hidden -Command "
|
|
228
|
+
await (0, util_os_1.reg)('add', WINDOWS_REG_KEY.replace('*', k) + '\\command', '/ve', '/f', '/d', `powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -Command "
|
|
229
229
|
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12;
|
|
230
230
|
$wsh = New-Object -ComObject Wscript.Shell;
|
|
231
231
|
$j = @{parent=@'\n${parent}\n'@; source=@'\n%1\n'@} | ConvertTo-Json -Compress
|
package/src/basicWeb.js
CHANGED
|
@@ -41,7 +41,7 @@ function basicWeb(ctx, node) {
|
|
|
41
41
|
const stream = (0, misc_1.asyncGeneratorToReadable)((0, misc_1.filterMapGenerator)(walker, async (el) => {
|
|
42
42
|
const isFolder = (0, vfs_1.nodeIsFolder)(el);
|
|
43
43
|
const name = (0, vfs_1.getNodeName)(el) + (isFolder ? '/' : '');
|
|
44
|
-
return `<li>${a((0, misc_1.pathEncode)(name) + (isFolder && !await (0, vfs_1.
|
|
44
|
+
return `<li>${a((0, misc_1.pathEncode)(name) + (isFolder && !await (0, vfs_1.getDefaultFile)(el, ctx) ? force : ''), name)}\n`;
|
|
45
45
|
}));
|
|
46
46
|
ctx.body = stream;
|
|
47
47
|
stream.push(`<meta name="viewport" content="width=device-width" />`);
|
package/src/commands.js
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
38
|
};
|
|
@@ -17,7 +50,8 @@ const plugins_1 = require("./plugins");
|
|
|
17
50
|
const fileAttr_1 = require("./fileAttr");
|
|
18
51
|
const github_1 = require("./github");
|
|
19
52
|
const cross_1 = require("./cross");
|
|
20
|
-
const api_monitor_1 =
|
|
53
|
+
const api_monitor_1 = __importStar(require("./api.monitor"));
|
|
54
|
+
const connections_1 = require("./connections");
|
|
21
55
|
const argv_1 = require("./argv");
|
|
22
56
|
const listen_2 = require("./listen");
|
|
23
57
|
let debugEnabled = argv_1.argv.debug || process.env.HFS_DEBUG || const_1.DEV;
|
|
@@ -191,11 +225,30 @@ const commands = {
|
|
|
191
225
|
params: '',
|
|
192
226
|
cb: fileAttr_1.purgeFileAttr,
|
|
193
227
|
},
|
|
228
|
+
transfers: {
|
|
229
|
+
params: '',
|
|
230
|
+
cb() {
|
|
231
|
+
const transfers = (0, connections_1.getConnections)().map(api_monitor_1.serializeConnection).filter(x => x.op === 'upload' || x.op === 'download');
|
|
232
|
+
if (!transfers.length)
|
|
233
|
+
return console.log("No ongoing uploads/downloads");
|
|
234
|
+
console.table(transfers.map(x => ({
|
|
235
|
+
type: x.op,
|
|
236
|
+
progress: (0, cross_1.with_)(x.opProgress ?? x.opOffset, v => v == null ? '' : (0, cross_1.formatPerc)(v)),
|
|
237
|
+
transferred: (0, cross_1.formatBytes)(Math.max(x.sent || 0, x.got || 0)),
|
|
238
|
+
total: x.opTotal == null ? '' : (0, cross_1.formatBytes)(x.opTotal),
|
|
239
|
+
speed: (0, cross_1.formatSpeed)(Math.max(x.outSpeedKb || 0, x.inSpeedKb || 0) * 1000),
|
|
240
|
+
user: x.user,
|
|
241
|
+
path: x.path,
|
|
242
|
+
})));
|
|
243
|
+
}
|
|
244
|
+
},
|
|
194
245
|
status: {
|
|
195
246
|
params: '',
|
|
196
247
|
async cb() {
|
|
197
248
|
const ports = await (0, listen_2.getServerStatus)(false);
|
|
198
249
|
console.log(lodash_1.default.map(ports, (x, k) => `${k.toUpperCase()} ${x.configuredPort < 0 ? "disabled" : x.listening ? `on port ${x.port}` : (x.error || "not working")}`).join(" – "));
|
|
250
|
+
const operations = lodash_1.default.countBy((0, connections_1.getConnections)(), x => x.ctx && (0, api_monitor_1.inferOperation)(x.ctx).op);
|
|
251
|
+
console.log(`Active downloads ↑ ${operations.download || 0} – uploads ↓ ${operations.upload || 0}`);
|
|
199
252
|
const conn = (await api_monitor_1.default.get_connection_stats().next()).value;
|
|
200
253
|
if (conn) {
|
|
201
254
|
const { sent_got: sg } = conn;
|
package/src/comments.js
CHANGED
|
@@ -16,6 +16,7 @@ const misc_1 = require("./misc");
|
|
|
16
16
|
const lodash_1 = __importDefault(require("lodash"));
|
|
17
17
|
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
|
18
18
|
const promises_1 = require("node:fs/promises");
|
|
19
|
+
const expiringCache_1 = require("./expiringCache");
|
|
19
20
|
exports.DESCRIPT_ION = 'descript.ion';
|
|
20
21
|
exports.DESCRIPT_ION_ALT = 'DESCRIPT.ION';
|
|
21
22
|
const commentsStorage = (0, config_1.defineConfig)(cross_1.CFG.comments_storage, '', v => ['', 'attr+ion'].includes(v)); // compiled tell us if we are using descript.ion
|
|
@@ -81,13 +82,15 @@ async function filePathHelper(folder) {
|
|
|
81
82
|
return await (0, util_files_1.exists)(alt) && !await (0, util_files_1.exists)(main) ? alt : main;
|
|
82
83
|
}
|
|
83
84
|
const MULTILINE_SUFFIX = Buffer.from([4, 0xC2]);
|
|
85
|
+
const pathCache = (0, expiringCache_1.expiringCache)(2_000);
|
|
86
|
+
// this can be called many times when listing a folder, and we want to also not check too often as it can be expensive, especially on a networked drive
|
|
84
87
|
async function readDescriptIon(path) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// for simplicity we "remove" the sequence MULTILINE_SUFFIX before iconv.decode messes it up
|
|
88
|
+
return (0, util_files_1.parseFile)(await pathCache.try(path, filePathHelper), raw => {
|
|
89
|
+
// for simplicity, we "remove" the sequence MULTILINE_SUFFIX before iconv.decode messes it up
|
|
88
90
|
for (let i = 0; i < raw.length; i++)
|
|
89
91
|
if (raw[i] === MULTILINE_SUFFIX[0] && raw[i + 1] === MULTILINE_SUFFIX[1] && [undefined, 13, 10].includes(raw[i + 2]))
|
|
90
92
|
raw[i] = raw[i + 1] = 10;
|
|
93
|
+
// decoding could also be done with native TextDecoder.decode, but we need iconv for the encoding anyway
|
|
91
94
|
const decoded = iconv_lite_1.default.decode(raw, descriptIonEncoding.get());
|
|
92
95
|
const ret = new Map(decoded.split('\n').map(line => {
|
|
93
96
|
const quoted = line[0] === '"' ? 1 : 0;
|
|
@@ -98,7 +101,7 @@ async function readDescriptIon(path) {
|
|
|
98
101
|
}));
|
|
99
102
|
ret.delete('');
|
|
100
103
|
return ret;
|
|
101
|
-
});
|
|
104
|
+
}, 2000);
|
|
102
105
|
}
|
|
103
106
|
descriptIonEncoding.sub(() => {
|
|
104
107
|
for (const k of util_files_1.parseFileCache.keys())
|
package/src/config.js
CHANGED
|
@@ -21,10 +21,8 @@ const fs_1 = require("fs");
|
|
|
21
21
|
const path_1 = require("path");
|
|
22
22
|
const events_1 = __importDefault(require("./events"));
|
|
23
23
|
const promises_1 = require("fs/promises");
|
|
24
|
-
const immer_1 = require("immer");
|
|
25
24
|
const argv_1 = require("./argv");
|
|
26
25
|
const util_files_1 = require("./util-files");
|
|
27
|
-
(0, immer_1.setAutoFreeze)(false); // we still want to mess with objects later (eg: account.belongs)
|
|
28
26
|
// keep definition of config properties
|
|
29
27
|
const configProps = {};
|
|
30
28
|
let started = false; // this will tell the difference for subscribeConfig()s that are called before or after config is loaded
|
|
@@ -74,6 +72,10 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
74
72
|
get() {
|
|
75
73
|
return getConfig(k);
|
|
76
74
|
},
|
|
75
|
+
async getWhenReady() {
|
|
76
|
+
await exports.configReady;
|
|
77
|
+
return this.get();
|
|
78
|
+
},
|
|
77
79
|
sub(cb) {
|
|
78
80
|
if (started) // initial event already passed, we'll make the first call
|
|
79
81
|
cb(getConfig(k), { k, was: defaultValue, defaultValue, version: configVersion.compiled(), object });
|
|
@@ -90,8 +92,10 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
90
92
|
}, { warnAfter: 1000 }); // e.g. each plugin watch enable_plugins
|
|
91
93
|
},
|
|
92
94
|
set(v) {
|
|
93
|
-
if (typeof v === 'function')
|
|
94
|
-
|
|
95
|
+
if (typeof v === 'function') {
|
|
96
|
+
const draft = structuredClone(this.get());
|
|
97
|
+
this.set(v(draft) ?? draft); // use return value if provided
|
|
98
|
+
}
|
|
95
99
|
else
|
|
96
100
|
setConfig1(k, v);
|
|
97
101
|
},
|
|
@@ -220,7 +224,7 @@ function subMultipleConfigs(cb, configs) {
|
|
|
220
224
|
};
|
|
221
225
|
}
|
|
222
226
|
exports.showHelp = argv_1.argv.help;
|
|
223
|
-
exports.configReady = events_1.default.once('configReady'); // the
|
|
227
|
+
exports.configReady = events_1.default.once('configReady').then(x => x[0]); // the value is startedWithoutConfig. The .then also avoids exposing the cancel-subscription function.
|
|
224
228
|
exports.configReady.then(() => {
|
|
225
229
|
if (!exports.showHelp)
|
|
226
230
|
return;
|
package/src/consoleLog.js
CHANGED
|
@@ -10,7 +10,16 @@ const cross_1 = require("./cross");
|
|
|
10
10
|
const fs_1 = require("fs");
|
|
11
11
|
const argv_1 = require("./argv");
|
|
12
12
|
exports.consoleLog = [];
|
|
13
|
+
const originalConsoleLog = console.log;
|
|
13
14
|
const f = argv_1.argv.consoleFile ? (0, fs_1.createWriteStream)(argv_1.argv.consoleFile, { flags: 'a', encoding: 'utf8' }) : null;
|
|
15
|
+
let terminalOutputBroken = false;
|
|
16
|
+
for (const stream of [process.stdout, process.stderr])
|
|
17
|
+
stream.on('error', err => {
|
|
18
|
+
if (!isBrokenTerminalOutput(err))
|
|
19
|
+
throw err;
|
|
20
|
+
// after the terminal/pipe is gone, further console writes would just re-emit the same process-level error
|
|
21
|
+
terminalOutputBroken = true;
|
|
22
|
+
});
|
|
14
23
|
for (const k of ['log', 'warn', 'error', 'debug']) {
|
|
15
24
|
const original = console[k];
|
|
16
25
|
console[k] = (...args) => {
|
|
@@ -28,7 +37,31 @@ for (const k of ['log', 'warn', 'error', 'debug']) {
|
|
|
28
37
|
if (k !== 'log')
|
|
29
38
|
args.unshift('!');
|
|
30
39
|
}
|
|
31
|
-
|
|
40
|
+
if (!terminalOutputBroken) {
|
|
41
|
+
try {
|
|
42
|
+
return original((0, cross_1.formatTime)(ts), ...args);
|
|
43
|
+
} // bundled nodejs doesn't have locales (and apparently uses en-US)
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (!isBrokenTerminalOutput(err))
|
|
46
|
+
throw err;
|
|
47
|
+
terminalOutputBroken = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
Object.assign(console[k], { original });
|
|
52
|
+
}
|
|
53
|
+
const over = console.log;
|
|
54
|
+
for (const k of ['table']) {
|
|
55
|
+
const original = console[k];
|
|
56
|
+
console[k] = (...args) => {
|
|
57
|
+
console.log = originalConsoleLog;
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
try {
|
|
60
|
+
return original(...args);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
console.log = over;
|
|
64
|
+
}
|
|
32
65
|
};
|
|
33
66
|
}
|
|
34
67
|
function safeJoin(a) {
|
|
@@ -55,6 +88,11 @@ function safeJoin(a) {
|
|
|
55
88
|
}).join(' ');
|
|
56
89
|
}
|
|
57
90
|
}
|
|
91
|
+
function isBrokenTerminalOutput(err) {
|
|
92
|
+
const code = err?.code;
|
|
93
|
+
return code === 'EPIPE' || code === 'EIO'
|
|
94
|
+
|| code === 'ERR_STREAM_DESTROYED' || code === 'ERR_STREAM_WRITE_AFTER_END';
|
|
95
|
+
}
|
|
58
96
|
function consoleHint(msg) {
|
|
59
97
|
console.log("HINT: " + msg);
|
|
60
98
|
}
|
package/src/const.js
CHANGED
|
@@ -78,6 +78,10 @@ exports.IS_BINARY = !/node|bun/.test((0, path_1.basename)(process.execPath)); //
|
|
|
78
78
|
exports.APP_PATH = (0, path_1.dirname)(exports.IS_BINARY ? process.execPath : __dirname); // __dirname's parent can be compared with cwd
|
|
79
79
|
exports.MIME_AUTO = 'auto';
|
|
80
80
|
exports.CONFIG_FILE = 'config.yaml';
|
|
81
|
+
if (argv_1.argv.version) {
|
|
82
|
+
process.stdout.write(exports.VERSION + '\n');
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
81
85
|
// we want this to be the first stuff to be printed, then we print it in this module, that is executed at the beginning
|
|
82
86
|
if (exports.DEV) {
|
|
83
87
|
console.clear();
|