hfs 3.2.2 → 3.3.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/{af-BDoqNBMg.js → af-CevhRske.js} +1 -1
- package/admin/assets/{am-DXBvKJ3X.js → am-4ouVTwuo.js} +1 -1
- package/admin/assets/{ar-BJIoxKTh.js → ar-71cK8dbb.js} +1 -1
- package/admin/assets/{ar-dz-C9l3uc_3.js → ar-dz-CO7oh1gG.js} +1 -1
- package/admin/assets/{ar-iq-DKH8u2lX.js → ar-iq-CjG6db8J.js} +1 -1
- package/admin/assets/{ar-kw-D3gJalQp.js → ar-kw-DMsdR225.js} +1 -1
- package/admin/assets/{ar-ly-BZJzWFeR.js → ar-ly-ZhxIwUOA.js} +1 -1
- package/admin/assets/{ar-ma-DabiOCr0.js → ar-ma-Bo3Z1RyK.js} +1 -1
- package/admin/assets/{ar-sa-C7suzSCY.js → ar-sa-Db44biae.js} +1 -1
- package/admin/assets/{ar-tn-BvBucEx0.js → ar-tn-DNWRixE0.js} +1 -1
- package/admin/assets/{az-CPUppH2M.js → az-B6LcmVzB.js} +1 -1
- package/admin/assets/{be-vzvhX2dl.js → be-Dp0KhO5x.js} +1 -1
- package/admin/assets/{bg-09Xbj_Y3.js → bg-TsOJJvBm.js} +1 -1
- package/admin/assets/{bi-CvhiuiE9.js → bi-DstczOGo.js} +1 -1
- package/admin/assets/{bm-DtJAZAkZ.js → bm-Bf09NN15.js} +1 -1
- package/admin/assets/{bn-Dnzh2x6I.js → bn-Da3tHLgx.js} +1 -1
- package/admin/assets/{bn-bd-xEy4797g.js → bn-bd-CeSalisC.js} +1 -1
- package/admin/assets/{bo-vuPmptG3.js → bo-Ctxe0IKQ.js} +1 -1
- package/admin/assets/{br-C9xH4OD3.js → br-Y3dPq3Tj.js} +1 -1
- package/admin/assets/{bs-BB_uCUp1.js → bs-BbAlSeZx.js} +1 -1
- package/admin/assets/{ca-D7LKaf98.js → ca-8-Nn97gi.js} +1 -1
- package/admin/assets/{cs-DMFTNdNC.js → cs-BD5ArFNW.js} +1 -1
- package/admin/assets/{cv-DNUjJwnO.js → cv-CdMFVXGr.js} +1 -1
- package/admin/assets/{cy-qmAOTPwC.js → cy-B3QEYlIT.js} +1 -1
- package/admin/assets/{da-DDX54iM9.js → da-Bju2YQ9G.js} +1 -1
- package/admin/assets/{de-BBmlsfbM.js → de-DdzCTquR.js} +1 -1
- package/admin/assets/{de-at-CuMZqYfu.js → de-at-DgyKCkPl.js} +1 -1
- package/admin/assets/{de-ch-s8OXKb-5.js → de-ch-Dk9qkzvn.js} +1 -1
- package/admin/assets/{dist-CHP3cW_n.js → dist-KY_8edrw.js} +1 -1
- package/admin/assets/{dv-Cjs3QDBN.js → dv-C5KWK8o4.js} +1 -1
- package/admin/assets/{el-BsAOrUDP.js → el-BDL_lPlM.js} +1 -1
- package/admin/assets/{en-DQ460ET7.js → en-Ds3H1Wec.js} +1 -1
- package/admin/assets/{en-au-8tMHM2OT.js → en-au-CgltfNUb.js} +1 -1
- package/admin/assets/{en-ca-c9jM4M_8.js → en-ca-DEsKzbWO.js} +1 -1
- package/admin/assets/{en-in-D2ULzXMp.js → en-gb-21AYC3Ik.js} +1 -1
- package/admin/assets/{en-ie-D4_e3kV6.js → en-ie-aN3g-yqK.js} +1 -1
- package/admin/assets/{en-il-BoaZOven.js → en-il-CZl2d7NK.js} +1 -1
- package/admin/assets/{en-tt-Cn72OR4N.js → en-in-C7Kv4c3w.js} +1 -1
- package/admin/assets/{en-nz-grrmB3dg.js → en-nz-DA_sIaGR.js} +1 -1
- package/admin/assets/{en-sg-Cmqxpoir.js → en-sg-CP5PJpZf.js} +1 -1
- package/admin/assets/{en-gb-unFIXx9f.js → en-tt-B5vDLhL7.js} +1 -1
- package/admin/assets/{eo-Cqxmc0TT.js → eo-VE1KDD1u.js} +1 -1
- package/admin/assets/{es-CkjKdFZ3.js → es-SUzi5oTU.js} +1 -1
- package/admin/assets/{es-do-CTcTshup.js → es-do-Iri0eWm8.js} +1 -1
- package/admin/assets/{es-mx-DMXgMMxs.js → es-mx-DwneKeOc.js} +1 -1
- package/admin/assets/{es-pr-DNCV91L3.js → es-pr-C4zPUdA1.js} +1 -1
- package/admin/assets/{es-us-C7o2mlIH.js → es-us-BPS67etc.js} +1 -1
- package/admin/assets/{esm-DvUbRuYx.js → esm-CyNQ7bBW.js} +1 -1
- package/admin/assets/{et-DaPpH-gK.js → et-Ciqbfae0.js} +1 -1
- package/admin/assets/{eu-DfKyFruu.js → eu-CGhOwQpe.js} +1 -1
- package/admin/assets/{fa-xWrBjePb.js → fa-BWZiAv_E.js} +1 -1
- package/admin/assets/{fi-BOWDMsDS.js → fi-DJmgkVsJ.js} +1 -1
- package/admin/assets/{fo-B3FupW8R.js → fo-DVez5N-g.js} +1 -1
- package/admin/assets/{fr-DZBDBkwp.js → fr-D206itJo.js} +1 -1
- package/admin/assets/{fr-ca-CRePPJJ5.js → fr-ca-C6JiEHfi.js} +1 -1
- package/admin/assets/{fr-ch-pFKvYU8D.js → fr-ch-DUTEuSU_.js} +1 -1
- package/admin/assets/{fy-0k7RzHUH.js → fy-C_r06zH-.js} +1 -1
- package/admin/assets/{ga-D31rB2ys.js → ga-CrdQqAN5.js} +1 -1
- package/admin/assets/{gd-CbgY-FFE.js → gd-iWHfhKAH.js} +1 -1
- package/admin/assets/{gl-DrtvS9gt.js → gl-zgfy-pQ0.js} +1 -1
- package/admin/assets/{gom-latn-Dkke3GyE.js → gom-latn-CMZ7Vj7V.js} +1 -1
- package/admin/assets/{gu-BlEipYhR.js → gu-DNqIMjZc.js} +1 -1
- package/admin/assets/{he-BrHT6OLB.js → he-OJBbYFzL.js} +1 -1
- package/admin/assets/{hi-Bx0zlAgr.js → hi-ByrMhKpX.js} +1 -1
- package/admin/assets/{hr-CoFYGbr6.js → hr-CfRZXzCu.js} +1 -1
- package/admin/assets/{ht-Df5xKo4a.js → ht-DDrMtLzJ.js} +1 -1
- package/admin/assets/{hu-BFTN3q6-.js → hu-Ckeq3R6_.js} +1 -1
- package/admin/assets/{hy-am-DuZTJQed.js → hy-am-OpZy7Nvg.js} +1 -1
- package/admin/assets/{id-DBZ5nPkf.js → id-BE8-Ys79.js} +1 -1
- package/admin/assets/{index-BGIe1xaR.js → index-34fRsXFH.js} +128 -123
- package/admin/assets/{is-CmeHm47B.js → is-BOxJcH5N.js} +1 -1
- package/admin/assets/{it-Cv3fjOA9.js → it-BFlrIXvP.js} +1 -1
- package/admin/assets/{it-ch-DCTyHA3T.js → it-ch-ILAp-lZZ.js} +1 -1
- package/admin/assets/{ja-DHNVXAM5.js → ja-CbbYAYyn.js} +1 -1
- package/admin/assets/{jv-BdNIxhWv.js → jv-CuLkRQCH.js} +1 -1
- package/admin/assets/{ka-D-joD0QB.js → ka-BFeRDN-I.js} +1 -1
- package/admin/assets/{kk-8wRNNjxz.js → kk-CMvQxaPk.js} +1 -1
- package/admin/assets/{km-dUWOg1uK.js → km-BdQbDUNF.js} +1 -1
- package/admin/assets/{kn-CwQbzel0.js → kn-CE5AS6Nx.js} +1 -1
- package/admin/assets/{ko-D_gn9Sfw.js → ko-Ctl1mHOt.js} +1 -1
- package/admin/assets/{ku-Dh3MRML3.js → ku-Blbo7opr.js} +1 -1
- package/admin/assets/{ky-CWYuRvil.js → ky-iCha_cZX.js} +1 -1
- package/admin/assets/{lb-Cc2xsSIx.js → lb-BaP0S6h1.js} +1 -1
- package/admin/assets/{lo-CkwNFDBz.js → lo-B_Q4zUMa.js} +1 -1
- package/admin/assets/{lt-HWyFDLXu.js → lt-BOaP1KGX.js} +1 -1
- package/admin/assets/{lv-C7Zx3OLO.js → lv-S1XMSUX1.js} +1 -1
- package/admin/assets/{me-DH3AqnfF.js → me-ChOLVJHC.js} +1 -1
- package/admin/assets/{mi-DMmUry6m.js → mi-CnoIUOGl.js} +1 -1
- package/admin/assets/{mk-D6MNZrzX.js → mk-BoEaxVXE.js} +1 -1
- package/admin/assets/{ml-B_ur8Zqf.js → ml-BuvmVn6P.js} +1 -1
- package/admin/assets/{mn-DeL8Ry7x.js → mn-2bvGd1HI.js} +1 -1
- package/admin/assets/{mr-C01qRXFL.js → mr-BAoAWQmy.js} +1 -1
- package/admin/assets/{ms-DFUZJzOm.js → ms-C2eT9r-f.js} +1 -1
- package/admin/assets/{ms-my-CUYHkebs.js → ms-my-D2ZXdcAf.js} +1 -1
- package/admin/assets/{mt-CE8t5jSj.js → mt-BL5JZC4T.js} +1 -1
- package/admin/assets/{my-Df96A8jZ.js → my-CuJ4L0Ji.js} +1 -1
- package/admin/assets/{nb-D0837ewu.js → nb-CjE6cT80.js} +1 -1
- package/admin/assets/{ne-BdK5TR0R.js → ne-BL9I0cb1.js} +1 -1
- package/admin/assets/{nl-oYKmSma6.js → nl-Dt3mtMln.js} +1 -1
- package/admin/assets/{nl-be-BfVC0cur.js → nl-be-Am1mjZK9.js} +1 -1
- package/admin/assets/{nn-IEUPnyc8.js → nn-ChSewyYy.js} +1 -1
- package/admin/assets/{oc-lnc-g_wSIROr.js → oc-lnc-x6_ChCE9.js} +1 -1
- package/admin/assets/{pa-in-gWhcLmZE.js → pa-in-DVyN8o2P.js} +1 -1
- package/admin/assets/{pl-H3-LoxVn.js → pl-CqcuAUGV.js} +1 -1
- package/admin/assets/{pt-hiX8RqVw.js → pt-B2BPpD7K.js} +1 -1
- package/admin/assets/{pt-br-B8UcNR6_.js → pt-br-oBVRyUzP.js} +1 -1
- package/admin/assets/{rn-BKupViLr.js → rn-B3e3nkfp.js} +1 -1
- package/admin/assets/{ro-DmhgIvml.js → ro-C2tWFx0Q.js} +1 -1
- package/admin/assets/{ru-CcpO-b8g.js → ru-CCsEUnFL.js} +1 -1
- package/admin/assets/{rw-DWX_5ZK0.js → rw-CaCkWlGA.js} +1 -1
- package/admin/assets/{sd-Dat8YgLw.js → sd-90Zixcez.js} +1 -1
- package/admin/assets/{se-nTBYCwx3.js → se-DaZXpLsK.js} +1 -1
- package/admin/assets/{sha512-CZz40GwW.js → sha512-DlGRFb_l.js} +1 -1
- package/admin/assets/{si-DG88w5El.js → si-CSoBAkgi.js} +1 -1
- package/admin/assets/{sk-s5FxW36J.js → sk-pgQaqa2z.js} +1 -1
- package/admin/assets/{sl-B-HyGrpA.js → sl-Cr5kSdA7.js} +1 -1
- package/admin/assets/{sq-DPFtfEC8.js → sq-Pxi2MZOP.js} +1 -1
- package/admin/assets/{sr-bBPg1zz4.js → sr-Fdc9-lC-.js} +1 -1
- package/admin/assets/{sr-cyrl-cXkYHMcP.js → sr-cyrl-06jNWqpg.js} +1 -1
- package/admin/assets/{ss-7nSqXJN7.js → ss-BjEzSMit.js} +1 -1
- package/admin/assets/{sv-B8mG1QK4.js → sv-CqWqUpcA.js} +1 -1
- package/admin/assets/{sv-fi-CotGRXSH.js → sv-fi-CsO2xQHd.js} +1 -1
- package/admin/assets/{sw-fAxU-Uck.js → sw-xQUdgYhn.js} +1 -1
- package/admin/assets/{ta-Dl0Z1pan.js → ta-epbFrZ1M.js} +1 -1
- package/admin/assets/{te-DmcKasFF.js → te-DusTwU6q.js} +1 -1
- package/admin/assets/{tet-D6mmvYpQ.js → tet-ylc9v4gl.js} +1 -1
- package/admin/assets/{tg-39Hf2-oO.js → tg-2QfVTAJf.js} +1 -1
- package/admin/assets/{th-BSmTJSFN.js → th-QIm0Mh7I.js} +1 -1
- package/admin/assets/{tk-m85E5igC.js → tk-140KMZeW.js} +1 -1
- package/admin/assets/{tl-ph-BlYxVvNt.js → tl-ph-S7IZkbx3.js} +1 -1
- package/admin/assets/{tlh-D1POnIeE.js → tlh-C2fYb34-.js} +1 -1
- package/admin/assets/{tr-BurtfrLy.js → tr-Be-IM6i4.js} +1 -1
- package/admin/assets/{tzl-BvyD0lOf.js → tzl-DMy30zde.js} +1 -1
- package/admin/assets/{tzm-DZHS0dK6.js → tzm-Cu882cHO.js} +1 -1
- package/admin/assets/{tzm-latn-yNW7CNCH.js → tzm-latn-BYxpYHet.js} +1 -1
- package/admin/assets/{ug-cn-rxs2pf5Y.js → ug-cn-Cl8Jqk2E.js} +1 -1
- package/admin/assets/{uk-CgZM-NnA.js → uk-B-8XymtC.js} +1 -1
- package/admin/assets/{ur-YxIR8yYD.js → ur-ChGuAlvQ.js} +1 -1
- package/admin/assets/{uz-CE9QsQMG.js → uz-QbfaW7fT.js} +1 -1
- package/admin/assets/{uz-latn-DiVdSW6G.js → uz-latn-DIQivLCy.js} +1 -1
- package/admin/assets/{vi-DKARV6qf.js → vi-C2CmYlDu.js} +1 -1
- package/admin/assets/{x-pseudo-BuAdOrXQ.js → x-pseudo-BWgm1XlP.js} +1 -1
- package/admin/assets/{yo-BcB85DhC.js → yo-8bNbPP6u.js} +1 -1
- package/admin/assets/{zh-DLmxfxzl.js → zh-CeNSRZ4r.js} +1 -1
- package/admin/assets/{zh-cn-ILRADZJi.js → zh-cn-BlbF5C_X.js} +1 -1
- package/admin/assets/{zh-hk-fT7fQk9_.js → zh-hk-kg1c_VSK.js} +1 -1
- package/admin/assets/{zh-tw-NqRXGP6v.js → zh-tw-BCl4UsPA.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{dist-legacy-BPToXNXu.js → dist-legacy-DYE1EOcc.js} +1 -1
- package/frontend/assets/index-legacy-Br-Lpsst.js +1 -0
- package/frontend/assets/{lib-legacy-CBk-_G90.js → lib-legacy-C4Gw_0kX.js} +1 -1
- package/frontend/assets/{sha512-legacy-CWrR2rkQ.js → sha512-legacy-D3RbPSpl.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/AsapStream.js +18 -14
- package/src/SendList.js +42 -16
- package/src/ThrottledStream.js +3 -3
- package/src/acme.js +23 -10
- package/src/adminApis.js +7 -6
- package/src/api.accounts.js +11 -7
- package/src/api.auth.js +8 -5
- package/src/api.cert.js +1 -1
- package/src/api.get_file_list.js +4 -3
- package/src/api.lang.js +3 -2
- package/src/api.monitor.js +1 -1
- package/src/api.net.js +3 -1
- package/src/api.plugins.js +2 -0
- package/src/api.vfs.js +5 -4
- package/src/apiMiddleware.js +5 -1
- package/src/basicWeb.js +1 -1
- package/src/block.js +1 -1
- package/src/commands.js +6 -5
- package/src/comments.js +29 -13
- package/src/config.js +7 -4
- package/src/consoleLog.js +5 -1
- package/src/cross-const.js +27 -1
- package/src/cross.js +32 -33
- package/src/ddns.js +1 -4
- package/src/events.js +1 -4
- package/src/fileAttr.js +1 -1
- package/src/frontEndApis.js +7 -3
- package/src/geo.js +8 -2
- package/src/github.js +10 -9
- package/src/index.js +1 -1
- package/src/ips.js +24 -3
- package/src/langs/hfs-lang-ar.json +1 -0
- package/src/langs/hfs-lang-bg.json +1 -0
- package/src/langs/hfs-lang-de.json +1 -0
- package/src/langs/hfs-lang-el.json +1 -0
- package/src/langs/hfs-lang-es.json +1 -0
- package/src/langs/hfs-lang-fi.json +1 -0
- package/src/langs/hfs-lang-fr.json +1 -0
- package/src/langs/hfs-lang-hu.json +1 -0
- package/src/langs/hfs-lang-it.json +1 -0
- package/src/langs/hfs-lang-ja.json +1 -0
- package/src/langs/hfs-lang-ko.json +1 -0
- package/src/langs/hfs-lang-lt.json +1 -0
- package/src/langs/hfs-lang-ms.json +1 -0
- package/src/langs/hfs-lang-nl.json +1 -0
- package/src/langs/hfs-lang-pl.json +1 -0
- package/src/langs/hfs-lang-pt-br.json +1 -0
- package/src/langs/hfs-lang-ro.json +1 -0
- package/src/langs/hfs-lang-ru.json +1 -0
- package/src/langs/hfs-lang-sr-latn.json +1 -0
- package/src/langs/hfs-lang-sr.json +1 -0
- package/src/langs/hfs-lang-th.json +1 -0
- package/src/langs/hfs-lang-tr.json +1 -0
- package/src/langs/hfs-lang-uk.json +1 -0
- package/src/langs/hfs-lang-vi.json +1 -0
- package/src/langs/hfs-lang-zh-tw.json +1 -0
- package/src/langs/hfs-lang-zh.json +1 -0
- package/src/listen.js +17 -13
- package/src/log.js +11 -2
- package/src/middlewares.js +4 -4
- package/src/misc.js +3 -3
- package/src/multipartUpload.js +1 -2
- package/src/nat.js +1 -1
- package/src/outboundProxy.js +7 -0
- package/src/perm.js +5 -3
- package/src/plugins.js +58 -36
- package/src/selfCheck.js +4 -3
- package/src/serveFile.js +3 -3
- package/src/serveGuiAndSharedFiles.js +7 -6
- package/src/serveGuiFiles.js +13 -1
- package/src/serverCode.js +27 -0
- package/src/throttler.js +3 -2
- package/src/update.js +9 -3
- package/src/upload.js +29 -11
- package/src/uploadOwners.js +95 -0
- package/src/util-files.js +4 -2
- package/src/util-http.js +3 -1
- package/src/vfs.js +28 -24
- package/src/walkDir.js +24 -11
- package/src/watchLoad.js +13 -2
- package/src/webdav.js +4 -2
- package/src/zip.js +1 -1
- package/frontend/assets/index-legacy-CmusDwvl.js +0 -1
package/src/api.accounts.js
CHANGED
|
@@ -11,25 +11,29 @@ const const_1 = require("./const");
|
|
|
11
11
|
const auth_1 = require("./auth");
|
|
12
12
|
const misc_1 = require("./misc");
|
|
13
13
|
const api_vfs_1 = require("./api.vfs");
|
|
14
|
-
function
|
|
15
|
-
|
|
14
|
+
function serializeAccount(ac) {
|
|
15
|
+
if (!ac)
|
|
16
|
+
return;
|
|
17
|
+
const hasLogin = (0, perm_1.accountHasLoginMethod)(ac);
|
|
18
|
+
return {
|
|
16
19
|
...lodash_1.default.omit(ac, ['password', 'hashed_password', 'srp']),
|
|
17
20
|
username: ac.username, // omit won't copy it because it's a hidden prop
|
|
18
21
|
hasPassword: (0, perm_1.accountHasPassword)(ac),
|
|
19
|
-
isGroup: !
|
|
22
|
+
isGroup: !hasLogin,
|
|
20
23
|
adminActualAccess: (0, perm_1.accountCanLoginAdmin)(ac),
|
|
21
|
-
canLogin:
|
|
24
|
+
canLogin: hasLogin ? (0, perm_1.accountCanLogin)(ac) : undefined,
|
|
22
25
|
canChangePassword: (0, perm_1.accountCanChangePassword)(ac),
|
|
23
26
|
invalidated: auth_1.invalidateSessionBefore.get(ac.username),
|
|
24
27
|
directMembers: Object.values(perm_1.accounts.get()).filter(a => a.belongs?.includes(ac.username)).map(x => x.username),
|
|
25
28
|
members: (0, misc_1.with_)(Object.values(perm_1.accounts.get()), accounts => {
|
|
26
29
|
const ret = [];
|
|
30
|
+
// breadth-first traversal follows indirect memberships and excludes already collected accounts
|
|
27
31
|
let news = [ac.username];
|
|
28
32
|
while (news.length) {
|
|
29
33
|
news = accounts.filter(a => !ret.includes(a.username) && a.belongs?.some(x => news.includes(x))).map(x => x.username);
|
|
30
34
|
ret.push(...news);
|
|
31
35
|
}
|
|
32
|
-
return
|
|
36
|
+
return ret.sort();
|
|
33
37
|
})
|
|
34
38
|
};
|
|
35
39
|
}
|
|
@@ -41,11 +45,11 @@ exports.default = {
|
|
|
41
45
|
},
|
|
42
46
|
get_account({ username }, ctx) {
|
|
43
47
|
(0, misc_1.apiAssertTypes)({ string_undefined: { username } });
|
|
44
|
-
return
|
|
48
|
+
return serializeAccount((0, perm_1.getAccount)(username || (0, auth_1.getCurrentUsername)(ctx)))
|
|
45
49
|
|| new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
46
50
|
},
|
|
47
51
|
get_accounts() {
|
|
48
|
-
return { list: (0, misc_1.onlyTruthy)(Object.values(perm_1.accounts.get()).map(
|
|
52
|
+
return { list: (0, misc_1.onlyTruthy)(Object.values(perm_1.accounts.get()).map(serializeAccount)) };
|
|
49
53
|
},
|
|
50
54
|
get_admins() {
|
|
51
55
|
return { list: lodash_1.default.filter(perm_1.accounts.get(), perm_1.accountCanLoginAdmin).map(ac => ac.username) };
|
package/src/api.auth.js
CHANGED
|
@@ -14,14 +14,18 @@ const auth_1 = require("./auth");
|
|
|
14
14
|
const config_1 = require("./config");
|
|
15
15
|
const events_1 = __importDefault(require("./events"));
|
|
16
16
|
const misc_1 = require("./misc");
|
|
17
|
+
const uploadOwners_1 = require("./uploadOwners");
|
|
17
18
|
const node_crypto_1 = require("node:crypto");
|
|
18
19
|
const ongoingLogins = {}; // store data that doesn't fit session object
|
|
19
|
-
const keepSessionAlive = (0, config_1.defineConfig)(
|
|
20
|
+
const keepSessionAlive = (0, config_1.defineConfig)(misc_1.CFG.keep_session_alive, true);
|
|
20
21
|
const fakeSrpSecret = (0, node_crypto_1.randomBytes)(32);
|
|
21
22
|
const refresh_session = async ({}, ctx) => {
|
|
23
|
+
if (!ctx.session)
|
|
24
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
|
25
|
+
(0, uploadOwners_1.getSessionId)(ctx); // anonymous upload ownership must be bound before any abortible upload request
|
|
22
26
|
const username = (0, auth_1.getCurrentUsername)(ctx);
|
|
23
27
|
const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx) || undefined;
|
|
24
|
-
return
|
|
28
|
+
return {
|
|
25
29
|
username,
|
|
26
30
|
expandedUsername: Array.from((0, perm_1.expandUsername)(username)),
|
|
27
31
|
isAdmin,
|
|
@@ -75,7 +79,7 @@ exports.authApis = {
|
|
|
75
79
|
// keep the public handshake identifier independent of predictable application PRNG state
|
|
76
80
|
const sid = (0, node_crypto_1.randomUUID)();
|
|
77
81
|
ongoingLogins[sid] = srpServer;
|
|
78
|
-
setTimeout(() => delete ongoingLogins[sid], 60_000);
|
|
82
|
+
setTimeout(() => delete ongoingLogins[sid], 60_000); // client must complete api sequence (loginSrp2) within 1 minute or will be discarded to avoid memory leaks
|
|
79
83
|
ctx.session.loggingIn = { username, sid }; // temporarily store until process is complete
|
|
80
84
|
return rest;
|
|
81
85
|
}
|
|
@@ -110,7 +114,7 @@ exports.authApis = {
|
|
|
110
114
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
111
115
|
try {
|
|
112
116
|
const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
|
|
113
|
-
.catch(() => { throw ''; });
|
|
117
|
+
.catch(() => { throw ''; }); // falsy value for later
|
|
114
118
|
await (0, auth_1.setLoggedIn)(ctx, username);
|
|
115
119
|
return {
|
|
116
120
|
proof: String(M2),
|
|
@@ -128,7 +132,6 @@ exports.authApis = {
|
|
|
128
132
|
delete ongoingLogins[sid];
|
|
129
133
|
}
|
|
130
134
|
},
|
|
131
|
-
// this api is here for consistency, but frontend is actually using
|
|
132
135
|
async logout({}, ctx) {
|
|
133
136
|
if (!ctx.session)
|
|
134
137
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
package/src/api.cert.js
CHANGED
|
@@ -17,7 +17,7 @@ exports.default = {
|
|
|
17
17
|
cert.validity.notBefore = new Date();
|
|
18
18
|
cert.validity.notAfter = new Date();
|
|
19
19
|
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
20
|
-
const attrs = Object.entries(attributes
|
|
20
|
+
const attrs = Object.entries(attributes ?? {}).map(x => ({ name: x[0], value: x[1] }));
|
|
21
21
|
cert.setSubject(attrs);
|
|
22
22
|
cert.setIssuer(attrs);
|
|
23
23
|
cert.sign(keys.privateKey);
|
package/src/api.get_file_list.js
CHANGED
|
@@ -75,7 +75,8 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
75
75
|
for await (const sub of walker) {
|
|
76
76
|
let name = (0, vfs_1.getNodeName)(sub);
|
|
77
77
|
name = (0, path_1.basename)(name) || name; // on Windows, basename('C:') === ''
|
|
78
|
-
if (filterName && !filterName(name)
|
|
78
|
+
if (filterName && !filterName(name)
|
|
79
|
+
|| fileMask && !(0, vfs_1.nodeIsFolder)(sub) && !fileMask(name)
|
|
79
80
|
|| filterComment && !filterComment(await (0, comments_1.getCommentFor)(sub.source) || ''))
|
|
80
81
|
continue;
|
|
81
82
|
const entry = await nodeToDirEntry(ctx, sub);
|
|
@@ -83,7 +84,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
83
84
|
continue;
|
|
84
85
|
const cbParams = { entry, ctx, listUri: uri, node: sub };
|
|
85
86
|
try {
|
|
86
|
-
const res = await Promise.all(onDirEntryHandlers.map(({ id, cb }) =>
|
|
87
|
+
const res = await Promise.all(onDirEntryHandlers.map(({ id, cb }) => (0, misc_1.callAsPromise)(() => cb(cbParams)).catch(error => { throw { id, error }; })));
|
|
87
88
|
if (res.some(x => x === false))
|
|
88
89
|
continue;
|
|
89
90
|
}
|
|
@@ -146,7 +147,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
146
147
|
};
|
|
147
148
|
}
|
|
148
149
|
catch {
|
|
149
|
-
return null;
|
|
150
|
+
return null; // ignore files with problems
|
|
150
151
|
}
|
|
151
152
|
function filesInsideCould(n = node) {
|
|
152
153
|
return (0, vfs_1.masksCouldGivePermission)(n.masks, 'can_read')
|
package/src/api.lang.js
CHANGED
|
@@ -48,8 +48,9 @@ const apis = {
|
|
|
48
48
|
code = (0, lang_1.file2code)(code);
|
|
49
49
|
validateCode(code);
|
|
50
50
|
const fn = (0, lang_1.code2file)(code);
|
|
51
|
-
const s =
|
|
52
|
-
|
|
51
|
+
const s = String(content);
|
|
52
|
+
const o = (0, misc_1.tryJson)(s);
|
|
53
|
+
if (!o?.translate)
|
|
53
54
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, "bad content for file " + fn);
|
|
54
55
|
await (0, promises_1.writeFile)(fn, s, 'utf8');
|
|
55
56
|
}
|
package/src/api.monitor.js
CHANGED
|
@@ -47,7 +47,7 @@ exports.default = {
|
|
|
47
47
|
connectionUpdated(conn, change) {
|
|
48
48
|
if (conn.socket.closed || lodash_1.default.isEmpty(change))
|
|
49
49
|
return;
|
|
50
|
-
if (change.ctx) {
|
|
50
|
+
if (change.ctx) { // serialize ctx once so all monitor subscribers reuse the resulting change
|
|
51
51
|
Object.assign(change, fromCtx(change.ctx));
|
|
52
52
|
change.ctx = undefined;
|
|
53
53
|
}
|
package/src/api.net.js
CHANGED
|
@@ -61,7 +61,7 @@ exports.default = {
|
|
|
61
61
|
if (external)
|
|
62
62
|
await (0, nat_1.getUpnpClient)().createMapping((0, nat_1.upnpMappingParam)(internal || internalPort, external))
|
|
63
63
|
.catch(res => {
|
|
64
|
-
throw new apiMiddleware_1.ApiError(
|
|
64
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, res.errorCode === 718 ? "Port not available" : res.errorDescription || res.message || "unknown error");
|
|
65
65
|
});
|
|
66
66
|
nat_1.mappedPort.set(external || 0); // remember only successful HFS mappings
|
|
67
67
|
return {};
|
|
@@ -84,6 +84,8 @@ exports.default = {
|
|
|
84
84
|
},
|
|
85
85
|
async make_cert({ domain, email, altNames }) {
|
|
86
86
|
(0, misc_1.apiAssertTypes)({ string: { domain }, string_undefined: { email }, array_undefined: { altNames } });
|
|
87
|
+
if (!domain)
|
|
88
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad params');
|
|
87
89
|
if (altNames?.some((name) => typeof name !== 'string'))
|
|
88
90
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad altNames');
|
|
89
91
|
await (0, acme_1.makeCert)(domain, email, altNames).catch(e => {
|
package/src/api.plugins.js
CHANGED
|
@@ -169,6 +169,8 @@ const apis = {
|
|
|
169
169
|
},
|
|
170
170
|
async uninstall_plugin({ id, deleteConfig }) {
|
|
171
171
|
assertPluginId(id);
|
|
172
|
+
if (!(0, plugins_1.getPluginInfo)(id))
|
|
173
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
172
174
|
await (0, plugins_1.stopPlugin)(id);
|
|
173
175
|
await (0, promises_1.rm)(plugins_1.PATH + '/' + id, { recursive: true, force: true });
|
|
174
176
|
if (deleteConfig)
|
package/src/api.vfs.js
CHANGED
|
@@ -26,7 +26,7 @@ async function urlToNodeOriginal(uri) {
|
|
|
26
26
|
exports.default = {
|
|
27
27
|
async get_vfs() {
|
|
28
28
|
return { root: await recur() };
|
|
29
|
-
async function recur(node = vfs_1.vfs) {
|
|
29
|
+
async function recur(node = vfs_1.vfs.compiled()) {
|
|
30
30
|
const { source } = node;
|
|
31
31
|
const stats = await (0, vfs_1.nodeStats)(node);
|
|
32
32
|
const isFolder = (0, vfs_1.nodeIsFolder)(node);
|
|
@@ -61,7 +61,7 @@ exports.default = {
|
|
|
61
61
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad name');
|
|
62
62
|
// check for siblings with the same name
|
|
63
63
|
const parent = await urlToNodeOriginal((0, path_1.dirname)(uri));
|
|
64
|
-
if (parent?.children?.find(x => (0, vfs_1.
|
|
64
|
+
if (parent?.children?.find(x => x !== n && (0, vfs_1.isSameFilenameAs)(props.name)(x)))
|
|
65
65
|
return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, 'name already present');
|
|
66
66
|
}
|
|
67
67
|
Object.assign(n, sanitizeVfsProps(props));
|
|
@@ -112,7 +112,7 @@ exports.default = {
|
|
|
112
112
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'name or source required');
|
|
113
113
|
if (name && !(0, misc_1.isValidFileName)(name))
|
|
114
114
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad name');
|
|
115
|
-
const parentNode = parent ? await urlToNodeOriginal(parent) : vfs_1.vfs;
|
|
115
|
+
const parentNode = parent ? await urlToNodeOriginal(parent) : vfs_1.vfs.compiled();
|
|
116
116
|
if (!parentNode)
|
|
117
117
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'parent not found');
|
|
118
118
|
if (!(0, vfs_1.nodeIsFolder)(parentNode))
|
|
@@ -190,9 +190,10 @@ exports.default = {
|
|
|
190
190
|
list.add({ n, k: 'd' });
|
|
191
191
|
}
|
|
192
192
|
catch (error) {
|
|
193
|
+
list.error(String(error));
|
|
193
194
|
console.debug(error);
|
|
194
195
|
}
|
|
195
|
-
return;
|
|
196
|
+
return list.close();
|
|
196
197
|
}
|
|
197
198
|
const sendPropsAsap = (0, util_os_1.getDiskSpace)(path).then(x => x && list.props(x));
|
|
198
199
|
try {
|
package/src/apiMiddleware.js
CHANGED
|
@@ -12,6 +12,7 @@ const misc_1 = require("./misc");
|
|
|
12
12
|
const const_1 = require("./const");
|
|
13
13
|
const config_1 = require("./config");
|
|
14
14
|
const plugins_1 = require("./plugins");
|
|
15
|
+
const SendList_1 = require("./SendList");
|
|
15
16
|
class ApiError extends Error {
|
|
16
17
|
status;
|
|
17
18
|
constructor(status, message) {
|
|
@@ -55,6 +56,9 @@ function apiMiddleware(apis) {
|
|
|
55
56
|
}
|
|
56
57
|
if (isAsyncGenerator(res))
|
|
57
58
|
res = (0, misc_1.asyncGeneratorToReadable)(res);
|
|
59
|
+
// return the initial list as regular JSON when the caller did not request live updates
|
|
60
|
+
if (res instanceof SendList_1.SendListReadable && ctx.get('accept') !== 'text/event-stream')
|
|
61
|
+
res = await res.collectInitial();
|
|
58
62
|
if (res instanceof stream_1.Readable) { // Readable, we'll go SSE-mode
|
|
59
63
|
res.pipe((0, sse_1.default)(ctx));
|
|
60
64
|
const resAsReadable = res; // satisfy ts
|
|
@@ -64,7 +68,7 @@ function apiMiddleware(apis) {
|
|
|
64
68
|
}
|
|
65
69
|
if (res instanceof ApiError)
|
|
66
70
|
return send(res.status, res.message);
|
|
67
|
-
if (res instanceof Error) // generic error/exception
|
|
71
|
+
if (res instanceof Error) // generic error/exception. expose unexpected errors to help diagnose bugs reported by users
|
|
68
72
|
return send(const_1.HTTP_BAD_REQUEST, res.stack || res.message || String(res));
|
|
69
73
|
ctx.body = res;
|
|
70
74
|
function send(status, body) {
|
package/src/basicWeb.js
CHANGED
|
@@ -13,7 +13,7 @@ const misc_1 = require("./misc");
|
|
|
13
13
|
const lodash_1 = __importDefault(require("lodash"));
|
|
14
14
|
const adminApis_1 = require("./adminApis");
|
|
15
15
|
const customHtml_1 = require("./customHtml");
|
|
16
|
-
const autoBasic = (0, config_1.defineConfig)(
|
|
16
|
+
const autoBasic = (0, config_1.defineConfig)(misc_1.CFG.auto_basic, true, v => lodash_1.default.isString(v) ? new RegExp(v, 'i') : null);
|
|
17
17
|
function basicWeb(ctx, node) {
|
|
18
18
|
const { get } = ctx.query;
|
|
19
19
|
if (get === 'login') {
|
package/src/block.js
CHANGED
|
@@ -13,7 +13,7 @@ const connections_1 = require("./connections");
|
|
|
13
13
|
const misc_1 = require("./misc");
|
|
14
14
|
const net_1 = require("net");
|
|
15
15
|
const lodash_1 = __importDefault(require("lodash"));
|
|
16
|
-
exports.block = (0, config_1.defineConfig)(
|
|
16
|
+
exports.block = (0, config_1.defineConfig)(misc_1.CFG.block, [], rules => {
|
|
17
17
|
const now = new Date();
|
|
18
18
|
const ret = !Array.isArray(rules) ? []
|
|
19
19
|
: (0, misc_1.onlyTruthy)(rules.map(rule => {
|
package/src/commands.js
CHANGED
|
@@ -54,7 +54,8 @@ const api_monitor_1 = __importStar(require("./api.monitor"));
|
|
|
54
54
|
const connections_1 = require("./connections");
|
|
55
55
|
const argv_1 = require("./argv");
|
|
56
56
|
const listen_2 = require("./listen");
|
|
57
|
-
|
|
57
|
+
const debug = (0, config_1.defineConfig)(cross_1.CFG.debug, Boolean(argv_1.argv.debug || process.env.HFS_DEBUG || const_1.DEV));
|
|
58
|
+
debug.dontStore();
|
|
58
59
|
if (!argv_1.argv.updating && !config_1.showHelp) {
|
|
59
60
|
try {
|
|
60
61
|
// Not sure if the try is necessary for when stdin is unavailable, but someone reported a problem using nohup https://github.com/rejetto/hfs/issues/74 and I've found this example try-catching https://github.com/DefinitelyTyped/DefinitelyTyped/blob/dda83a906914489e09ca28afea12948529015d4a/types/node/readline.d.ts#L489
|
|
@@ -80,7 +81,7 @@ if (!argv_1.argv.updating && !config_1.showHelp) {
|
|
|
80
81
|
for (const k of ['log', 'warn', 'error', 'debug']) {
|
|
81
82
|
const original = console[k];
|
|
82
83
|
console[k] = (...args) => {
|
|
83
|
-
if (k === 'debug' && !
|
|
84
|
+
if (k === 'debug' && !debug.get())
|
|
84
85
|
return;
|
|
85
86
|
if (!first_1.quitting && tty)
|
|
86
87
|
clean();
|
|
@@ -96,7 +97,7 @@ if (!argv_1.argv.updating && !config_1.showHelp) {
|
|
|
96
97
|
catch {
|
|
97
98
|
console.log("Console commands not available");
|
|
98
99
|
const original = console.debug;
|
|
99
|
-
console.debug = (...args) =>
|
|
100
|
+
console.debug = (...args) => debug.get() && original(...args);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
async function parseCommandLine(line) {
|
|
@@ -198,8 +199,8 @@ const commands = {
|
|
|
198
199
|
debug: {
|
|
199
200
|
params: '',
|
|
200
201
|
cb() {
|
|
201
|
-
|
|
202
|
-
console.log(`Debug messages ${
|
|
202
|
+
debug.set(v => !v);
|
|
203
|
+
console.log(`Debug messages ${debug.get() ? "on" : "off"}`);
|
|
203
204
|
}
|
|
204
205
|
},
|
|
205
206
|
'start-plugin': {
|
package/src/comments.js
CHANGED
|
@@ -16,15 +16,16 @@ 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 promises_2 = require("stream/promises");
|
|
19
20
|
const expiringCache_1 = require("./expiringCache");
|
|
20
21
|
exports.DESCRIPT_ION = 'descript.ion';
|
|
21
22
|
exports.DESCRIPT_ION_ALT = 'DESCRIPT.ION';
|
|
22
23
|
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
|
|
23
|
-
(0, config_1.defineConfig)(
|
|
24
|
+
(0, config_1.defineConfig)(cross_1.CFG.descript_ion, true, (v, more) => {
|
|
24
25
|
if (!v && more.version?.olderThan('0.57.0-alpha1'))
|
|
25
26
|
commentsStorage.set('attr');
|
|
26
27
|
});
|
|
27
|
-
const descriptIonEncoding = (0, config_1.defineConfig)(
|
|
28
|
+
const descriptIonEncoding = (0, config_1.defineConfig)(cross_1.CFG.descript_ion_encoding, 'utf8');
|
|
28
29
|
function readFromDescriptIon(path) {
|
|
29
30
|
return usingDescriptIon() && readDescriptIon((0, path_1.dirname)(path)).then(x => x.get((0, path_1.basename)(path)), () => undefined);
|
|
30
31
|
}
|
|
@@ -39,16 +40,29 @@ async function getCommentFor(path) {
|
|
|
39
40
|
]).then(([fromAttr, fromIon]) => fromAttr || fromIon || undefined);
|
|
40
41
|
}
|
|
41
42
|
async function setCommentFor(path, comment) {
|
|
42
|
-
if (commentsStorage.get())
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if (commentsStorage.get())
|
|
44
|
+
return prep((0, misc_1.storeFileAttr)(path, COMMENT_ATTR, comment || undefined)).then(v => {
|
|
45
|
+
if (v)
|
|
46
|
+
void setCommentDescriptIon(path, '').catch(() => { });
|
|
47
|
+
return v;
|
|
48
|
+
});
|
|
49
|
+
// should we also remove from file-attr, similarly as above we remove from descript.ion? not sure, but for the time we won't because #1 storeFileAttr is not really deleting, and we would store a lot of empty attributes, #2 more people will switch from descript.ion to attr (because introduced later) than the opposite
|
|
50
|
+
return prep(setCommentDescriptIon(path, comment));
|
|
51
|
+
function prep(p) {
|
|
52
|
+
return p.then(v => v !== false, (e) => {
|
|
53
|
+
console.error(`${comment ? "Error setting comment" : "Error removing comment"}: ${String(e)}`);
|
|
54
|
+
return false;
|
|
55
|
+
});
|
|
45
56
|
}
|
|
46
|
-
return setCommentDescriptIon(path, comment); // should we also remove from file-attr? not sure, but for the time we won't because #1 storeFileAttr is not really deleting, and we would store a lot of empty attributes, #2 more people will switch from descript.ion to attr (because introduced later) than the opposite
|
|
47
57
|
}
|
|
48
|
-
const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(
|
|
49
|
-
const byFolder = lodash_1.default.groupBy(jobs, job => (0, path_1.dirname)(job[0]));
|
|
50
|
-
|
|
51
|
-
const comments = await readDescriptIon(folder).catch(
|
|
58
|
+
const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)((jobs) => {
|
|
59
|
+
const byFolder = lodash_1.default.groupBy(jobs, job => (0, path_1.dirname)(job[0])); // jobs in the same folder share one write and its success or failure
|
|
60
|
+
const resultByFolder = lodash_1.default.mapValues(byFolder, async (jobs, folder) => {
|
|
61
|
+
const comments = await readDescriptIon(folder).catch(e => {
|
|
62
|
+
if (e?.code !== 'ENOENT')
|
|
63
|
+
throw e;
|
|
64
|
+
return new Map();
|
|
65
|
+
});
|
|
52
66
|
for (const [path, comment] of jobs) {
|
|
53
67
|
const file = path.slice(folder.length + 1);
|
|
54
68
|
if (!comment)
|
|
@@ -58,7 +72,7 @@ const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(async (job
|
|
|
58
72
|
}
|
|
59
73
|
const path = await filePathHelper(folder);
|
|
60
74
|
if (!comments.size)
|
|
61
|
-
return (0, promises_1.
|
|
75
|
+
return (0, promises_1.rm)(path, { force: true });
|
|
62
76
|
// encode comments in descript.ion format
|
|
63
77
|
const ws = await (0, util_files_1.createSafeWriteStream)(path);
|
|
64
78
|
comments.forEach((comment, filename) => {
|
|
@@ -70,8 +84,10 @@ const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(async (job
|
|
|
70
84
|
ws.write(MULTILINE_SUFFIX, 'binary');
|
|
71
85
|
ws.write('\n');
|
|
72
86
|
});
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
ws.end();
|
|
88
|
+
await (0, promises_2.finished)(ws);
|
|
89
|
+
});
|
|
90
|
+
return jobs.map(([path]) => resultByFolder[(0, path_1.dirname)(path)]);
|
|
75
91
|
});
|
|
76
92
|
function areCommentsEnabled() {
|
|
77
93
|
return true; // true since we introduced comments in file-attr
|
package/src/config.js
CHANGED
|
@@ -12,6 +12,7 @@ exports.getWholeConfig = getWholeConfig;
|
|
|
12
12
|
exports.setConfig = setConfig;
|
|
13
13
|
exports.subMultipleConfigs = subMultipleConfigs;
|
|
14
14
|
const const_1 = require("./const");
|
|
15
|
+
const cross_const_1 = require("./cross-const");
|
|
15
16
|
const watchLoad_1 = require("./watchLoad");
|
|
16
17
|
const yaml_1 = __importDefault(require("yaml"));
|
|
17
18
|
const lodash_1 = __importDefault(require("lodash"));
|
|
@@ -25,6 +26,7 @@ const argv_1 = require("./argv");
|
|
|
25
26
|
const util_files_1 = require("./util-files");
|
|
26
27
|
// keep definition of config properties
|
|
27
28
|
const configProps = {};
|
|
29
|
+
const dontStore = [];
|
|
28
30
|
let started = false; // this will tell the difference for subscribeConfig()s that are called before or after config is loaded
|
|
29
31
|
let state = {}; // current state of config properties
|
|
30
32
|
const filePath = (0, cross_1.with_)(argv_1.argv.config || process.env.HFS_CONFIG, p => {
|
|
@@ -62,7 +64,7 @@ class Version extends String {
|
|
|
62
64
|
exports.Version = Version;
|
|
63
65
|
const CONFIG_CHANGE_EVENT_PREFIX = 'config.';
|
|
64
66
|
exports.currentVersion = new Version(const_1.VERSION);
|
|
65
|
-
const configVersion = defineConfig(
|
|
67
|
+
const configVersion = defineConfig(cross_const_1.CFG.version, const_1.VERSION, v => new Version(v));
|
|
66
68
|
function defineConfig(k, defaultValue, compiler) {
|
|
67
69
|
configProps[k] = { defaultValue };
|
|
68
70
|
const object = {
|
|
@@ -104,7 +106,8 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
104
106
|
compiled = v;
|
|
105
107
|
const was = getConfig(k);
|
|
106
108
|
return events_1.default.emitAsync(CONFIG_CHANGE_EVENT_PREFIX + k, was, was, const_1.VERSION, true);
|
|
107
|
-
}
|
|
109
|
+
},
|
|
110
|
+
dontStore() { dontStore.push(k); },
|
|
108
111
|
};
|
|
109
112
|
let compiled = compiler?.(defaultValue, { k, version: exports.currentVersion, defaultValue, object });
|
|
110
113
|
if (compiler)
|
|
@@ -185,11 +188,11 @@ const saveDebounced = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
|
185
188
|
const aWeekAgo = Date.now() - cross_1.DAY * 7;
|
|
186
189
|
if (await (0, util_files_1.statWithTimeout)(bak).then(x => aWeekAgo > x.mtimeMs, () => true))
|
|
187
190
|
await (0, promises_1.copyFile)(filePath, bak).catch(() => { }); // ignore errors
|
|
188
|
-
await exports.configFile.save(stringify({
|
|
191
|
+
await exports.configFile.save(stringify(lodash_1.default.omit({
|
|
189
192
|
...state,
|
|
190
193
|
version: const_1.VERSION,
|
|
191
194
|
platform: `${process.platform}-${process.arch}${(0, cross_1.prefix)('-', !const_1.IS_BINARY && (0, path_1.basename)(process.execPath))}`,
|
|
192
|
-
})).catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
|
|
195
|
+
}, dontStore))).catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
|
|
193
196
|
});
|
|
194
197
|
const saveConfigAsap = () => void saveDebounced();
|
|
195
198
|
exports.saveConfigAsap = saveConfigAsap;
|
package/src/consoleLog.js
CHANGED
|
@@ -11,7 +11,11 @@ const fs_1 = require("fs");
|
|
|
11
11
|
const argv_1 = require("./argv");
|
|
12
12
|
exports.consoleLog = [];
|
|
13
13
|
const originalConsoleLog = console.log;
|
|
14
|
-
|
|
14
|
+
let f = argv_1.argv.consoleFile ? (0, fs_1.createWriteStream)(argv_1.argv.consoleFile, { flags: 'a', encoding: 'utf8' }) : null;
|
|
15
|
+
f?.on('error', err => {
|
|
16
|
+
f = null; // stop using a failed stream so later logs cannot repeat the same error
|
|
17
|
+
console.error("Cannot write console file", argv_1.argv.consoleFile, String(err));
|
|
18
|
+
});
|
|
15
19
|
let terminalOutputBroken = false;
|
|
16
20
|
for (const stream of [process.stdout, process.stderr])
|
|
17
21
|
stream.on('error', err => {
|
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.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.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_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.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;
|
|
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.UPLOAD_TEMP_PREFIX = 'hfs$upload-';
|
|
14
15
|
exports.UPLOAD_TEMP_HASH = 'upload-temp-hash';
|
|
15
16
|
exports.MTIME_CHECK = 'x-mtime-check';
|
|
16
17
|
exports.PREVIOUS_TAG = 'previous';
|
|
@@ -19,6 +20,31 @@ exports.HIDE_IN_TESTS = 'hideInTests'; // elements that have variable size, wher
|
|
|
19
20
|
exports.MASK_IN_TESTS = 'maskInTests';
|
|
20
21
|
exports.EMBEDDED_LANGUAGE = 'en'; // frontend includes this language in the code, and not need to import the translation-json
|
|
21
22
|
exports.BASIC_AUTHENTICATE_HEADER = 'Basic realm="HFS"';
|
|
23
|
+
exports.CFG = constMap([
|
|
24
|
+
'accounts', 'acme_domain', 'acme_renew', 'admin_net', 'allowed_referer',
|
|
25
|
+
'authorization_header', 'auto_basic', 'auto_check_update', 'auto_play_seconds',
|
|
26
|
+
'base_url', 'block', 'cache_control_disk_files', 'cert', 'comments_storage',
|
|
27
|
+
'create-admin', 'debug', 'delete_unfinished_uploads_after', 'descript_ion',
|
|
28
|
+
'descript_ion_encoding', 'disable_custom_html', 'disableTranslation', 'dont_log_net',
|
|
29
|
+
'dont_overwrite_uploading', 'dynamic_dns_url', 'enable_plugins', 'error_log',
|
|
30
|
+
'favicon', 'file_menu_on_link', 'file_timeout', 'folders_first', 'force_address',
|
|
31
|
+
'force_https', 'force_lang', 'force_webdav_login',
|
|
32
|
+
'geo_allow', 'geo_allow_unknown', 'geo_enable', 'geo_ignore_net', 'geo_list',
|
|
33
|
+
'ignore_proxies', 'invert_order', 'keep_session_alive', 'listen_interface',
|
|
34
|
+
'localhost_admin', 'log', 'log_api', 'log_gui', 'log_host', 'log_rotation', 'log_spam',
|
|
35
|
+
'log_ua', 'mapped_port', 'max_downloads', 'max_downloads_per_account',
|
|
36
|
+
'max_downloads_per_ip', 'max_kbps', 'max_kbps_per_ip', 'menu_at_top', 'mime',
|
|
37
|
+
'min_available_mb', 'open_browser_at_start', 'outbound_interface', 'outbound_proxy', 'own_upload_delete_hours',
|
|
38
|
+
'page_size', 'plugins_config', 'port', 'private_key', 'proxies', 'roots', 'server_code',
|
|
39
|
+
'session_duration', 'show_hidden_files', 'show_uploader', 'size_1024',
|
|
40
|
+
'smart_unc_folder_detection', 'sort_by', 'sort_numerics', 'split_uploads',
|
|
41
|
+
'suspend_plugins', 'theme', 'tile_size', 'title', 'title_with_path', 'track_ips',
|
|
42
|
+
'update_to_beta', 'upnp_enabled', 'version', 'vfs', 'webdav_initial_auth',
|
|
43
|
+
'zip_calculate_size_for_seconds', 'https_port'
|
|
44
|
+
]);
|
|
45
|
+
function constMap(a) {
|
|
46
|
+
return Object.fromEntries(a.map(x => [x, x]));
|
|
47
|
+
}
|
|
22
48
|
exports.HTTP_OK = 200;
|
|
23
49
|
exports.HTTP_CREATED = 201;
|
|
24
50
|
exports.HTTP_NO_CONTENT = 204;
|
package/src/cross.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.VFS_STORED_KEYS = exports.PERM_KEYS = exports.defaultPerms = exports.WHO_ADMIN = exports.WHO_ANY_ACCOUNT = exports.WHO_NO_ONE = exports.WHO_ANYONE = exports.LIST = exports.
|
|
20
|
+
exports.VFS_STORED_KEYS = exports.PERM_KEYS = exports.defaultPerms = exports.WHO_ADMIN = exports.WHO_ANY_ACCOUNT = exports.WHO_NO_ONE = exports.WHO_ANYONE = exports.LIST = exports.THEME_OPTIONS = exports.SORT_BY_OPTIONS = exports.FRONTEND_OPTIONS = exports.MAX_TILE_SIZE = exports.DAY = exports.HOUR = exports.MINUTE = exports.WIKI_URL = exports.REPO_URL = exports.WEBSITE = void 0;
|
|
21
21
|
exports.isWhoObject = isWhoObject;
|
|
22
22
|
exports.formatBytes = formatBytes;
|
|
23
23
|
exports.formatSpeed = formatSpeed;
|
|
@@ -92,6 +92,7 @@ exports.safeDecodeURIComponent = safeDecodeURIComponent;
|
|
|
92
92
|
exports.popKey = popKey;
|
|
93
93
|
exports.patchKey = patchKey;
|
|
94
94
|
exports.retry = retry;
|
|
95
|
+
exports.callAsPromise = callAsPromise;
|
|
95
96
|
exports.toMutable = toMutable;
|
|
96
97
|
exports.shortenAgent = shortenAgent;
|
|
97
98
|
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
@@ -108,26 +109,21 @@ exports.HOUR = 60 * exports.MINUTE;
|
|
|
108
109
|
exports.DAY = 24 * exports.HOUR;
|
|
109
110
|
exports.MAX_TILE_SIZE = 10;
|
|
110
111
|
exports.FRONTEND_OPTIONS = {
|
|
111
|
-
file_menu_on_link: true,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
[cross_const_1.CFG.file_menu_on_link]: true,
|
|
113
|
+
[cross_const_1.CFG.menu_at_top]: false,
|
|
114
|
+
[cross_const_1.CFG.tile_size]: 0,
|
|
115
|
+
[cross_const_1.CFG.page_size]: 100,
|
|
116
|
+
[cross_const_1.CFG.sort_by]: 'name',
|
|
117
|
+
[cross_const_1.CFG.invert_order]: false,
|
|
118
|
+
[cross_const_1.CFG.folders_first]: true,
|
|
119
|
+
[cross_const_1.CFG.sort_numerics]: false,
|
|
120
|
+
[cross_const_1.CFG.title_with_path]: true,
|
|
121
|
+
[cross_const_1.CFG.theme]: '',
|
|
122
|
+
[cross_const_1.CFG.auto_play_seconds]: 5,
|
|
123
|
+
[cross_const_1.CFG.disableTranslation]: false,
|
|
122
124
|
};
|
|
123
125
|
exports.SORT_BY_OPTIONS = ['name', 'extension', 'size', 'time', 'creation'];
|
|
124
126
|
exports.THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' };
|
|
125
|
-
// had found an interesting way to infer a type from all the calls to defineConfig (by the literals passed), but would not be usable also by admin-panel
|
|
126
|
-
exports.CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown', 'dynamic_dns_url',
|
|
127
|
-
'log', 'error_log', 'log_rotation', 'dont_log_net', 'log_gui', 'log_api', 'log_ua', 'log_spam', 'track_ips',
|
|
128
|
-
'max_downloads', 'max_downloads_per_ip', 'max_downloads_per_account', 'roots', 'force_address', 'split_uploads',
|
|
129
|
-
'force_lang', 'suspend_plugins', 'base_url', 'size_1024', 'disable_custom_html', 'comments_storage',
|
|
130
|
-
'force_webdav_login', 'webdav_initial_auth', 'outbound_proxy', 'mapped_port', 'upnp_enabled', 'show_uploader']);
|
|
131
127
|
exports.LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' };
|
|
132
128
|
exports.WHO_ANYONE = true;
|
|
133
129
|
exports.WHO_NO_ONE = false;
|
|
@@ -144,9 +140,6 @@ exports.defaultPerms = {
|
|
|
144
140
|
exports.PERM_KEYS = typedKeys(exports.defaultPerms);
|
|
145
141
|
exports.VFS_STORED_KEYS = ['name', 'source', 'masks', 'default', 'accept', 'rename',
|
|
146
142
|
'mime', 'url', 'target', 'comment', 'icon', 'order', 'children', ...exports.PERM_KEYS];
|
|
147
|
-
function constMap(a) {
|
|
148
|
-
return Object.fromEntries(a.map(x => [x, x]));
|
|
149
|
-
}
|
|
150
143
|
function isWhoObject(v) {
|
|
151
144
|
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
152
145
|
}
|
|
@@ -190,13 +183,13 @@ function haveTimeout(ms, job, error) {
|
|
|
190
183
|
function objFromKeys(src, getValue) {
|
|
191
184
|
return Object.fromEntries(src.map(k => [k, getValue(k)]));
|
|
192
185
|
}
|
|
193
|
-
function enforceFinal(sub, s, evenEmpty = false) {
|
|
186
|
+
function enforceFinal(sub, s = '', evenEmpty = false) {
|
|
194
187
|
return (s ? !s.endsWith(sub) : evenEmpty) ? s + sub : s;
|
|
195
188
|
}
|
|
196
189
|
function removeFinal(sub, s) {
|
|
197
190
|
return s.endsWith(sub) ? s.slice(0, -sub.length) : s;
|
|
198
191
|
}
|
|
199
|
-
function enforceStarting(sub, s, evenEmpty = false) {
|
|
192
|
+
function enforceStarting(sub, s = '', evenEmpty = false) {
|
|
200
193
|
return (s ? !s.startsWith(sub) : evenEmpty) ? sub + s : s;
|
|
201
194
|
}
|
|
202
195
|
function removeStarting(sub, s) {
|
|
@@ -451,7 +444,7 @@ function xlate(input, table) {
|
|
|
451
444
|
}
|
|
452
445
|
// remove brackets and port (if any)
|
|
453
446
|
function normalizeHost(host) {
|
|
454
|
-
return host[0] === '[' ? host.slice(1, host.indexOf(']')) : host
|
|
447
|
+
return host[0] === '[' ? host.slice(1, host.indexOf(']')) : host.split(':')[0];
|
|
455
448
|
}
|
|
456
449
|
function isIpLocalHost(ip) {
|
|
457
450
|
return ip === '::1' || ip.endsWith('127.0.0.1');
|
|
@@ -565,16 +558,22 @@ function patchKey(o, k, replacer) {
|
|
|
565
558
|
o[k] = replacer(o[k]);
|
|
566
559
|
return o;
|
|
567
560
|
}
|
|
568
|
-
//
|
|
561
|
+
// retry rejected operations because filesystem resources may be released late
|
|
569
562
|
async function retry(cb, delay = 1000) {
|
|
570
|
-
let
|
|
571
|
-
while (true)
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
563
|
+
let retries = 3;
|
|
564
|
+
while (true)
|
|
565
|
+
try {
|
|
566
|
+
return await cb();
|
|
567
|
+
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
if (!retries--)
|
|
570
|
+
throw e;
|
|
571
|
+
await wait(delay);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// normalize callback results and synchronous throws into a promise
|
|
575
|
+
function callAsPromise(callback) {
|
|
576
|
+
return Promise.resolve().then(callback);
|
|
578
577
|
}
|
|
579
578
|
function toMutable(value) {
|
|
580
579
|
return Array.isArray(value) ? value.slice() : { ...value };
|